package dods.clients.importwizard; import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; /** * This class display a hierarchial list of Dods URLs. Inventories are shown * at the top level, and Data URLs selected from those inventories are shown * as children of the inventories. The list supports basic operations such * as removing Data URLs or inventories, and throws events as * <code>TreeSelectionEvent</code>s to allow for more complex manipulation * of URLs. * * @author Rich Honhart <rhonhart@po.gso.uri.edu> */ public class URLList extends JPanel implements ActionListener { private JTree urlTree; private DefaultTreeModel treeModel; private DefaultMutableTreeNode top; private DefaultMutableTreeNode single; private Vector listeners; private JScrollPane scroller; private Hashtable nodes; private int urlCount; /** * Create a new URLList. */ URLList() { setLayout(new BorderLayout()); top = new DefaultMutableTreeNode("Datasets"); nodes = new Hashtable(); urlTree = new JTree(top); treeModel = (DefaultTreeModel)urlTree.getModel(); scroller = new JScrollPane(urlTree); listeners = new Vector(); urlCount = 0; // Create the popup menu for items in the list. JMenuItem menuItem; JPopupMenu urlPopup = new JPopupMenu(); menuItem = new JMenuItem("Delete Selected"); menuItem.addActionListener(this); menuItem.setActionCommand("deleteSelected"); urlPopup.add(menuItem); // Create the popup menu for inventories in the list. JPopupMenu invPopup = new JPopupMenu(); menuItem = new JMenuItem("Remove Inventory"); menuItem.addActionListener(this); menuItem.setActionCommand("removeInventory"); invPopup.add(menuItem); invPopup.addSeparator(); menuItem = new JMenuItem("Select data URLs"); menuItem.addActionListener(this); menuItem.setActionCommand("selectURLs"); invPopup.add(menuItem); menuItem = new JMenuItem("Remove data URLs"); menuItem.addActionListener(this); menuItem.setActionCommand("deleteURLs"); invPopup.add(menuItem); MouseListener popupListener = new TreePopupListener(urlTree, urlPopup, invPopup); urlTree.addMouseListener(popupListener); add(scroller, BorderLayout.CENTER); } /** * adjust the viewport so that it's all the way to the left. */ private void adjustViewport() { JViewport viewport = scroller.getViewport(); Point p = viewport.getViewPosition(); p.x = 0; viewport.setViewPosition(p); scroller.setViewport(viewport); } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); if(command.equals("deleteSelected")) { removeSelectedURLs(); } else if(command.equals("removeInventory")) { TreePath t = urlTree.getSelectionPath(); if(t != null) { DefaultMutableTreeNode n = (DefaultMutableTreeNode)t.getLastPathComponent(); urlCount -= n.getChildCount(); n.removeAllChildren(); treeModel.removeNodeFromParent(n); } } else if(command.equals("selectURLs")) { TreePath parent = urlTree.getSelectionPath(); if(parent != null) { DefaultMutableTreeNode n = (DefaultMutableTreeNode)parent.getLastPathComponent(); DefaultMutableTreeNode temp; int ind = 0; TreePath[] t = new TreePath[n.getChildCount()]; Enumeration children = n.children(); while(children.hasMoreElements()) { temp = (DefaultMutableTreeNode)children.nextElement(); t[ind++] = parent.pathByAddingChild(temp); } urlTree.setSelectionPaths(t); } } else if(command.equals("deleteURLs")) { TreePath t = urlTree.getSelectionPath(); if(t != null) { DefaultMutableTreeNode n = (DefaultMutableTreeNode)t.getLastPathComponent(); urlCount -= n.getChildCount(); n.removeAllChildren(); } treeModel.reload(); } } /** * Add an inventory to the URLList. * @param inv The <code>DodsURL</code> where the inventory can be found. */ public void addInventory(DodsURL inv) { DefaultMutableTreeNode temp = new DefaultMutableTreeNode(inv); treeModel.insertNodeInto(temp, top, top.getChildCount()); TreePath t = new TreePath(treeModel.getPathToRoot(top)); nodes.put(inv, temp); t = t.pathByAddingChild(temp); urlTree.setSelectionPath(t); urlTree.scrollPathToVisible(t); adjustViewport(); } /** * Add a <code>TreeSelectionListener</code> * @param t The TreeSelectionListener */ public void addTreeSelectionListener(TreeSelectionListener t) { urlTree.addTreeSelectionListener(t); listeners.addElement(t); } /** * Add a URL to the URLList. This URL will be added to the group * "Single URL's". * @param url The URL. */ public void addURL(DodsURL url) { if(single == null) { single = new DefaultMutableTreeNode("Single URLs"); treeModel.insertNodeInto(single, top, top.getChildCount()); } DefaultMutableTreeNode leafNode = new DefaultMutableTreeNode(url); TreePath t = new TreePath(treeModel.getPathToRoot(single)); treeModel.insertNodeInto(new DefaultMutableTreeNode(url), single, single.getChildCount()); urlCount++; t = t.pathByAddingChild(leafNode); urlTree.setSelectionPath(t); urlTree.scrollPathToVisible(t); adjustViewport(); } /** * Adds a URL to the URLList to be associated with the inventory * inv. If inv hasn't been added to the list yet, or was not added * as an inventory, this method will do nothing. * @param url The URL to add. * @param inv The inventory to add it to. */ public void addURLToInventory(DodsURL url, DodsURL inv) { if(nodes.containsKey(inv)) { DefaultMutableTreeNode invNode = (DefaultMutableTreeNode)nodes.get(inv); TreePath t = new TreePath(treeModel.getPathToRoot(invNode)); DefaultMutableTreeNode leafNode = new DefaultMutableTreeNode(url); treeModel.insertNodeInto(leafNode, invNode, invNode.getChildCount()); urlCount++; t = t.pathByAddingChild(leafNode); urlTree.setSelectionPath(t); urlTree.scrollPathToVisible(t); adjustViewport(); } } /** * Add multiple URLs to an inventory. If that inventory doesn't exist, * this method won't do anything * @param newURLs The URLs to add. * @param inv The inventory to add it to. */ public void addURLsToInventory(DodsURL[] newURLs, DodsURL inv) { if(nodes.containsKey(inv)) { DefaultMutableTreeNode invNode = (DefaultMutableTreeNode)nodes.get(inv); TreePath parentPath = new TreePath(treeModel.getPathToRoot(invNode)); TreePath[] t = new TreePath[newURLs.length]; for(int i=0;i<newURLs.length;i++) { DefaultMutableTreeNode leafNode = new DefaultMutableTreeNode(newURLs[i]); treeModel.insertNodeInto(leafNode, invNode, invNode.getChildCount()); t[i] = parentPath.pathByAddingChild(leafNode); } urlCount += newURLs.length; urlTree.setSelectionPaths(t); urlTree.scrollPathToVisible(t[0]); adjustViewport(); } } /** * Return all the Data URLs from the URLList * @return All the Data URLs from the URLList. */ public DodsURL[] getURLs() { // Can't use nodes here, because we want to get the single urls Enumeration inventories = top.children(); DodsURL[] ret = new DodsURL[urlCount]; int ind = 0; // This makes the assumption that there are only two levels to // the tree, based on the assumption that there are never going // to be inventories of inventories. If support for that is ever // added, then this will have to change. while(inventories.hasMoreElements()) { DefaultMutableTreeNode inv = (DefaultMutableTreeNode)inventories.nextElement(); Enumeration children = inv.children(); while(children.hasMoreElements()) { DefaultMutableTreeNode child = (DefaultMutableTreeNode)children.nextElement(); ret[ind++] = (DodsURL)child.getUserObject(); } } return ret; } /** * Returns the number of Data URLs in the list. * @return the number of Data URLs in the list. */ public int getURLCount() { return urlCount; } /** * Returns the number of Data URLs associated with an inventory. * @param inv The inventory URL. * @return The number of URLs associated with inv. */ public int getURLCount(DodsURL inv) { if(nodes.containsKey(inv)) { MutableTreeNode n = (MutableTreeNode)nodes.get(inv); return n.getChildCount(); } return 0; } /** * Returns the first selected URL from the list. * @return the first selected URL from the list. */ public Object getSelectedValue() { TreePath t = urlTree.getSelectionPath(); if(t != null) { return ((DefaultMutableTreeNode)t.getLastPathComponent()).getUserObject(); } else return null; } /** * Returns all the selected URLs from the list. * @return all the selected URLs from the list. */ public Object[] getSelectedValues() { TreePath[] t = urlTree.getSelectionPaths(); Object[] ret = null; if(t != null) { ret = new Object[t.length]; for(int i=0;i<t.length;i++) { ret[i] = ((DefaultMutableTreeNode)t[i].getLastPathComponent()).getUserObject(); } } return ret; } /** * Removes all the Data URLs that have been selected from an inventory. * @param inv The inventory whose URLs will be removed. */ public void removeAllURLsFromInventory(DodsURL inv) { DefaultMutableTreeNode n = (DefaultMutableTreeNode)nodes.get(inv); if(n != null) { urlCount -= n.getChildCount(); n.removeAllChildren(); } treeModel.reload(); } /** * Removes all the selected URLs. */ public void removeSelectedURLs() { // FIXME: This is really slow for any significant number of URLs. TreePath[] t = urlTree.getSelectionPaths(); TreeNode parent = null; if(t.length > 0) { parent = (TreeNode) t[0].getPathComponent(t[0].getPathCount() - 2); } // Remove all the TreeSelectionListeners before removing the // nodes so that URLBuilder doesn't try to update the interface // for every node that's deleted. for(int i=0;i<listeners.size();i++) urlTree.removeTreeSelectionListener((TreeSelectionListener)listeners.elementAt(i)); for(int i=0;i<t.length-1;i++) { treeModel.removeNodeFromParent((MutableTreeNode)t[i].getLastPathComponent()); urlCount--; } // Before we delete the last node, add the selection listener back // so that one event will be generated. for(int i=0;i<listeners.size();i++) urlTree.addTreeSelectionListener((TreeSelectionListener)listeners.elementAt(i)); if(t.length > 0) { treeModel.removeNodeFromParent((MutableTreeNode)t[t.length-1].getLastPathComponent()); urlCount--; } if(parent == single && single.getChildCount() == 0) { treeModel.removeNodeFromParent(single); single = null; } } }