| /* |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| package org.apache.batik.apps.svgbrowser; |
| |
| import java.awt.Component; |
| import java.awt.Graphics; |
| 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.datatransfer.UnsupportedFlavorException; |
| import java.awt.dnd.Autoscroll; |
| import java.awt.dnd.DnDConstants; |
| import java.awt.dnd.DragGestureEvent; |
| import java.awt.dnd.DragGestureListener; |
| import java.awt.dnd.DragGestureRecognizer; |
| 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.dnd.DropTarget; |
| import java.awt.dnd.DropTargetContext; |
| import java.awt.dnd.DropTargetDragEvent; |
| import java.awt.dnd.DropTargetDropEvent; |
| import java.awt.dnd.DropTargetEvent; |
| import java.awt.dnd.DropTargetListener; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.EventListener; |
| import java.util.EventObject; |
| import java.util.Iterator; |
| |
| import javax.swing.JPanel; |
| import javax.swing.JRootPane; |
| import javax.swing.JTree; |
| import javax.swing.JViewport; |
| import javax.swing.SwingUtilities; |
| import javax.swing.Timer; |
| import javax.swing.UIManager; |
| import javax.swing.event.EventListenerList; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.TreeNode; |
| import javax.swing.tree.TreePath; |
| |
| import org.apache.batik.dom.util.DOMUtilities; |
| import org.apache.batik.apps.svgbrowser.DOMViewer.NodeInfo; |
| |
| import org.w3c.dom.Node; |
| |
| /** |
| * A swing tree to represent DOM Document. |
| */ |
| public class DOMDocumentTree extends JTree implements Autoscroll { |
| |
| /** |
| * Listeners list. |
| */ |
| protected EventListenerList eventListeners = new EventListenerList(); |
| |
| /** |
| * The insets where autoscrolling is active. |
| */ |
| protected Insets autoscrollInsets = new Insets(20, 20, 20, 20); |
| |
| /** |
| * How much to scroll. |
| */ |
| protected Insets scrollUnits = new Insets(25, 25, 25, 25); |
| |
| /** |
| * The controller for this tree. |
| */ |
| protected DOMDocumentTreeController controller; |
| |
| /** |
| * Creates the DOMDocumentTree. |
| * |
| * @param root |
| * Root node |
| * @param controller |
| * The tree controller |
| */ |
| public DOMDocumentTree(TreeNode root, DOMDocumentTreeController controller) { |
| super(root); |
| this.controller = controller; |
| new TreeDragSource(this, DnDConstants.ACTION_COPY_OR_MOVE); |
| new DropTarget(this, new TreeDropTargetListener(this)); |
| } |
| |
| // DND Support |
| |
| /** |
| * The JTree drag source wrapper. |
| */ |
| public class TreeDragSource implements DragSourceListener, |
| DragGestureListener { |
| |
| /** |
| * The drag source. |
| */ |
| protected DragSource source; |
| |
| /** |
| * The drag gesture recognizer. |
| */ |
| protected DragGestureRecognizer recognizer; |
| |
| /** |
| * The transferable tree node(s). |
| */ |
| protected TransferableTreeNode transferable; |
| |
| /** |
| * The sourceTree. |
| */ |
| protected DOMDocumentTree sourceTree; |
| |
| /** |
| * Constructor. |
| * |
| * @param tree |
| * The source tree |
| * @param actions |
| * The permitted action |
| */ |
| public TreeDragSource(DOMDocumentTree tree, int actions) { |
| sourceTree = tree; |
| source = new DragSource(); |
| recognizer = |
| source.createDefaultDragGestureRecognizer(sourceTree, actions, |
| this); |
| } |
| |
| public void dragGestureRecognized(DragGestureEvent dge) { |
| if (!controller.isDNDSupported()) { |
| return; |
| } |
| TreePath[] paths = sourceTree.getSelectionPaths(); |
| // If an empty selection is 'being dragged' |
| if (paths == null) { |
| return; |
| } |
| ArrayList nodeList = new ArrayList(); |
| for (int i = 0; i < paths.length; i++) { |
| TreePath path = paths[i]; |
| // If the root node 'being dragged' |
| if (path.getPathCount() > 1) { |
| DefaultMutableTreeNode node = |
| (DefaultMutableTreeNode) path.getLastPathComponent(); |
| Node associatedNode = getDomNodeFromTreeNode(node); |
| if (associatedNode != null) { |
| nodeList.add(associatedNode); |
| } |
| } |
| } |
| if (nodeList.isEmpty()) { |
| return; |
| } |
| transferable = new TransferableTreeNode(new TransferData(nodeList)); |
| |
| // Sets the default cursor behavior |
| source.startDrag(dge, null, transferable, this); |
| } |
| |
| public void dragEnter(DragSourceDragEvent dsde) { |
| } |
| |
| public void dragExit(DragSourceEvent dse) { |
| } |
| |
| public void dragOver(DragSourceDragEvent dsde) { |
| } |
| |
| public void dropActionChanged(DragSourceDragEvent dsde) { |
| } |
| |
| public void dragDropEnd(DragSourceDropEvent dsde) { |
| } |
| } |
| |
| /** |
| * Tree as a drop target listener. |
| */ |
| public class TreeDropTargetListener implements DropTargetListener { |
| |
| /** |
| * Insert node before the current node. |
| */ |
| private static final int BEFORE = 1; |
| |
| /** |
| * Insert node after the current node. |
| */ |
| private static final int AFTER = 2; |
| |
| /** |
| * Insert node as a child of the current node. |
| */ |
| private static final int CURRENT = 3; |
| |
| /** |
| * The associated transfer data. |
| */ |
| private TransferData transferData; |
| |
| /** |
| * The original glass pane of the tree is stored here. |
| */ |
| private Component originalGlassPane; |
| |
| /** |
| * The vertical offset where to catch the 'visual tips' of the tree node |
| * items rectangle. |
| */ |
| private int visualTipOffset = 5; |
| |
| /** |
| * The thickness of the visual tip. |
| */ |
| private int visualTipThickness = 2; |
| |
| /** |
| * Indicates the potential drop position relative to the current node |
| * where the dragged nodes are to be inserted. |
| */ |
| private int positionIndicator; |
| |
| /** |
| * The start point of the 'visual tip' line. |
| */ |
| private Point startPoint; |
| |
| /** |
| * The end point of the 'visual tip' line. |
| */ |
| private Point endPoint; |
| |
| /** |
| * Glasspane where 'visual tip' line is drawn |
| */ |
| protected JPanel visualTipGlassPane = new JPanel() { |
| public void paint(Graphics g) { |
| g.setColor(UIManager.getColor("Tree.selectionBackground")); |
| if (startPoint == null || endPoint == null) { |
| return; |
| } |
| int x1 = startPoint.x; |
| int x2 = endPoint.x; |
| int y1 = startPoint.y; |
| |
| // Draws the visualTipThickness number of lines |
| int start = -visualTipThickness / 2; |
| start += visualTipThickness % 2 == 0 ? 1 : 0; |
| for (int i = start; i <= visualTipThickness / 2; i++) { |
| g.drawLine(x1 + 2, y1 + i, x2 - 2, y1 + i); |
| } |
| } |
| }; |
| |
| /** |
| * The timer that controls the delay of expanding the tree path that is |
| * being dragged over. |
| */ |
| private Timer expandControlTimer; |
| |
| /** |
| * The delay for expanding. |
| */ |
| private int expandTimeout = 1500; |
| |
| /** |
| * The tree path that is being dragged over. |
| */ |
| private TreePath dragOverTreePath; |
| |
| /** |
| * The tree path that is scheduled for expand. |
| */ |
| private TreePath treePathToExpand; |
| |
| /** |
| * Constructor. |
| */ |
| public TreeDropTargetListener(DOMDocumentTree tree) { |
| addOnAutoscrollListener(tree); |
| } |
| |
| public void dragEnter(DropTargetDragEvent dtde) { |
| JTree tree = (JTree) dtde.getDropTargetContext().getComponent(); |
| JRootPane rootPane = tree.getRootPane(); |
| // Set glass pane |
| originalGlassPane = rootPane.getGlassPane(); |
| rootPane.setGlassPane(visualTipGlassPane); |
| visualTipGlassPane.setOpaque(false); |
| visualTipGlassPane.setVisible(true); |
| updateVisualTipLine(tree, null); |
| // Set transferable |
| try { |
| // XXX Java 1.3 and 1.4 workaround for: |
| // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4378091 |
| Transferable transferable = |
| new DropTargetDropEvent(dtde.getDropTargetContext(), |
| dtde.getLocation(), 0, 0) |
| .getTransferable(); |
| // Transferable transferable = dtde.getTransferable(); |
| DataFlavor[] flavors = transferable.getTransferDataFlavors(); |
| for (int i = 0; i < flavors.length; i++) { |
| if (transferable.isDataFlavorSupported(flavors[i])) { |
| transferData = (TransferData) transferable |
| .getTransferData(flavors[i]); |
| return; |
| } |
| } |
| } catch (UnsupportedFlavorException e) { |
| e.printStackTrace(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public void dragOver(DropTargetDragEvent dtde) { |
| JTree tree = (JTree) dtde.getDropTargetContext().getComponent(); |
| TreeNode targetTreeNode = getNode(dtde); |
| if (targetTreeNode != null) { |
| // Get the parent and sibling paths and nodes |
| updatePositionIndicator(dtde); |
| Point p = dtde.getLocation(); |
| TreePath currentPath = tree.getPathForLocation(p.x, p.y); |
| TreePath parentPath = getParentPathForPosition(currentPath); |
| TreeNode parentNode = getNodeForPath(parentPath); |
| TreePath nextSiblingPath = |
| getSiblingPathForPosition(currentPath); |
| TreeNode nextSiblingNode = getNodeForPath(nextSiblingPath); |
| Node potentialParent = |
| getDomNodeFromTreeNode((DefaultMutableTreeNode) parentNode); |
| Node potentialSibling = |
| getDomNodeFromTreeNode |
| ((DefaultMutableTreeNode) nextSiblingNode); |
| // Check the drop target: |
| // - Checks if any node from the dragged nodes can be appended |
| // to the parent node |
| // - Checks whether the sibling node is among the nodes being |
| // dragged |
| if (DOMUtilities.canAppendAny(transferData.getNodeList(), |
| potentialParent) |
| && !transferData.getNodeList() |
| .contains(potentialSibling)) { |
| dtde.acceptDrag(dtde.getDropAction()); |
| // Draw the 'visual tip' line |
| updateVisualTipLine(tree, currentPath); |
| // Expand the path |
| dragOverTreePath = currentPath; |
| if (!tree.isExpanded(currentPath)) { |
| scheduleExpand(currentPath, tree); |
| } |
| } else { |
| dtde.rejectDrag(); |
| } |
| } else { |
| dtde.rejectDrag(); |
| } |
| } |
| |
| public void dropActionChanged(DropTargetDragEvent dtde) { |
| } |
| |
| public void drop(DropTargetDropEvent dtde) { |
| Point p = dtde.getLocation(); |
| DropTargetContext dtc = dtde.getDropTargetContext(); |
| JTree tree = (JTree) dtc.getComponent(); |
| // Sets the original glass pane |
| setOriginalGlassPane(tree); |
| // Cancel tree item expanding |
| dragOverTreePath = null; |
| // Get the parent and sibling paths and nodes |
| TreePath currentPath = tree.getPathForLocation(p.x, p.y); |
| DefaultMutableTreeNode parent = |
| (DefaultMutableTreeNode) getNodeForPath |
| (getParentPathForPosition(currentPath)); |
| Node dropTargetNode = getDomNodeFromTreeNode(parent); |
| DefaultMutableTreeNode sibling = |
| (DefaultMutableTreeNode) |
| getNodeForPath(getSiblingPathForPosition(currentPath)); |
| Node siblingNode = getDomNodeFromTreeNode(sibling); |
| if (this.transferData != null) { |
| ArrayList nodelist = |
| getNodeListForParent(this.transferData.getNodeList(), |
| dropTargetNode); |
| fireDropCompleted |
| (new DOMDocumentTreeEvent |
| (new DropCompletedInfo |
| (dropTargetNode, siblingNode, nodelist))); |
| dtde.dropComplete(true); |
| return; |
| } |
| dtde.rejectDrop(); |
| } |
| |
| public void dragExit(DropTargetEvent dte) { |
| setOriginalGlassPane |
| ((JTree) dte.getDropTargetContext().getComponent()); |
| // Set the current dragover path |
| dragOverTreePath = null; |
| } |
| |
| /** |
| * Sets the position indicator according to the current cursor location. |
| * |
| * @param dtde |
| * DropTargetDragEvent |
| */ |
| private void updatePositionIndicator(DropTargetDragEvent dtde) { |
| Point p = dtde.getLocation(); |
| DropTargetContext dtc = dtde.getDropTargetContext(); |
| JTree tree = (JTree) dtc.getComponent(); |
| // Current path |
| TreePath currentPath = tree.getPathForLocation(p.x, p.y); |
| Rectangle bounds = tree.getPathBounds(currentPath); |
| // Upper area of the tree node |
| if (p.y <= bounds.y + visualTipOffset) { |
| positionIndicator = BEFORE; |
| } |
| // Lower area of the tree node |
| else if (p.y >= bounds.y + bounds.height - visualTipOffset) { |
| positionIndicator = AFTER; |
| } |
| // Somewhere between the upper and the lower area of the tree node |
| else { |
| positionIndicator = CURRENT; |
| } |
| } |
| |
| /** |
| * Finds the parent TreePath of the given current path, according to the |
| * position indicator, where the dragged nodes should be appended. |
| * |
| * @param currentPath |
| * The current path (the items are dragged over this path) |
| * @param positionIndicator |
| * AFTER or BEFORE - nodes should be appended to the parent |
| * path of the given path, as siblings of the current path |
| * CURRENT - nodes should be appended to the current path, as |
| * its children |
| * @return TreePath where dragged nodes are to be inserted |
| */ |
| private TreePath getParentPathForPosition(TreePath currentPath) { |
| if (currentPath == null) { |
| return null; |
| } |
| TreePath parentPath = null; |
| if (positionIndicator == AFTER) { |
| parentPath = currentPath.getParentPath(); |
| } else if (positionIndicator == BEFORE) { |
| parentPath = currentPath.getParentPath(); |
| } else if (positionIndicator == CURRENT) { |
| parentPath = currentPath; |
| } |
| return parentPath; |
| } |
| |
| /** |
| * Finds the TreePath that is going to be next sibling to the nodes that |
| * are being dragged. |
| * |
| * @param currentPath |
| * The current path (the items are dragged over this path) |
| * @return sibling TreePath |
| */ |
| private TreePath getSiblingPathForPosition(TreePath currentPath) { |
| TreePath parentPath = getParentPathForPosition(currentPath); |
| TreePath nextSiblingPath = null; |
| if (positionIndicator == AFTER) { |
| TreeNode parentNode = getNodeForPath(parentPath); |
| TreeNode currentNode = getNodeForPath(currentPath); |
| if (parentPath != null && parentNode != null |
| && currentNode != null) { |
| int siblingIndex = parentNode.getIndex(currentNode) + 1; |
| if (parentNode.getChildCount() > siblingIndex) { |
| nextSiblingPath = |
| parentPath.pathByAddingChild |
| (parentNode.getChildAt(siblingIndex)); |
| } |
| } |
| } else if (positionIndicator == BEFORE) { |
| nextSiblingPath = currentPath; |
| } else if (positionIndicator == CURRENT) { |
| nextSiblingPath = null; |
| } |
| return nextSiblingPath; |
| } |
| |
| /** |
| * Gets the TreeNode from the given TreePath. |
| * |
| * @param path |
| * The given TreePath |
| * @return The TreeNode |
| */ |
| private TreeNode getNodeForPath(TreePath path) { |
| if (path == null || path.getLastPathComponent() == null) { |
| return null; |
| } |
| return (TreeNode) path.getLastPathComponent(); |
| } |
| |
| /** |
| * Gets the TreeNode from the DropTargetDragEvent |
| * |
| * @param dtde |
| * The DropTargetDragEvent |
| * @return Associated TreeNode or null |
| */ |
| private TreeNode getNode(DropTargetDragEvent dtde) { |
| Point p = dtde.getLocation(); |
| DropTargetContext dtc = dtde.getDropTargetContext(); |
| JTree tree = (JTree) dtc.getComponent(); |
| TreePath path = tree.getPathForLocation(p.x, p.y); |
| if (path == null || path.getLastPathComponent() == null) { |
| return null; |
| } |
| return (TreeNode) path.getLastPathComponent(); |
| } |
| |
| // Visual tips |
| /** |
| * Draws the 'visual tip' line on the glass pane. |
| * |
| * @param tree |
| * The tree |
| * @param path |
| * The path to get the bounds |
| */ |
| private void updateVisualTipLine(JTree tree, TreePath path) { |
| if (path == null) { |
| startPoint = null; |
| endPoint = null; |
| } else { |
| Rectangle bounds = tree.getPathBounds(path); |
| if (positionIndicator == BEFORE) { |
| startPoint = bounds.getLocation(); |
| endPoint = new Point(startPoint.x + bounds.width, |
| startPoint.y); |
| } else if (positionIndicator == AFTER) { |
| startPoint = new Point(bounds.x, bounds.y + bounds.height); |
| endPoint = new Point(startPoint.x + bounds.width, |
| startPoint.y); |
| positionIndicator = AFTER; |
| } else if (positionIndicator == CURRENT) { |
| startPoint = null; |
| endPoint = null; |
| } |
| if (startPoint != null && endPoint != null) { |
| startPoint = SwingUtilities.convertPoint(tree, startPoint, |
| visualTipGlassPane); |
| endPoint = SwingUtilities.convertPoint(tree, endPoint, |
| visualTipGlassPane); |
| } |
| } |
| visualTipGlassPane.getRootPane().repaint(); |
| } |
| |
| /** |
| * Adds the onAutoscroll listener. |
| * |
| * @param tree |
| * The DOMDocumentTree |
| */ |
| private void addOnAutoscrollListener(DOMDocumentTree tree) { |
| tree.addListener(new DOMDocumentTreeAdapter() { |
| public void onAutoscroll(DOMDocumentTreeEvent event) { |
| // Whenever autoscroll is triggered, |
| // the 'visual tip' line should be hidden |
| startPoint = null; |
| endPoint = null; |
| } |
| }); |
| } |
| |
| /** |
| * Sets the original glass pane. |
| * |
| * @param dte |
| * DropTargetEvent to get the tree |
| */ |
| private void setOriginalGlassPane(JTree tree) { |
| JRootPane rootPane = tree.getRootPane(); |
| rootPane.setGlassPane(originalGlassPane); |
| originalGlassPane.setVisible(false); |
| rootPane.repaint(); |
| } |
| |
| // Expand scheduling |
| /** |
| * Schedules the expand of the given treePath on a tree. |
| * |
| * @param treePath |
| * The treePath to expand |
| * @param tree |
| * The JTree |
| */ |
| private void scheduleExpand(TreePath treePath, JTree tree) { |
| // If the treepath to schedule for expand isn't already scheduled |
| if (treePath != treePathToExpand) { |
| getExpandTreeTimer(tree).stop(); |
| treePathToExpand = treePath; |
| getExpandTreeTimer(tree).start(); |
| } |
| } |
| |
| /** |
| * Gets the timer for treepath expand. |
| * |
| * @param tree |
| * The JTree |
| * @return Timer |
| */ |
| private Timer getExpandTreeTimer(final JTree tree) { |
| if (expandControlTimer == null) { |
| expandControlTimer = new Timer(expandTimeout, |
| new ActionListener() { |
| public void actionPerformed(ActionEvent arg0) { |
| // If the treepath scheduled for expand is the |
| // same one that is being dragged over |
| if (treePathToExpand != null |
| && treePathToExpand == dragOverTreePath) { |
| tree.expandPath(treePathToExpand); |
| } |
| getExpandTreeTimer(tree).stop(); |
| } |
| }); |
| } |
| return expandControlTimer; |
| } |
| } |
| |
| /** |
| * Transferable tree node. |
| */ |
| public static class TransferableTreeNode implements Transferable { |
| |
| /** |
| * A flavor that supports the node transfer. |
| */ |
| protected static final DataFlavor NODE_FLAVOR = |
| new DataFlavor(TransferData.class, "TransferData"); |
| |
| /** |
| * The supported flavors. |
| */ |
| protected static final DataFlavor[] FLAVORS = |
| new DataFlavor[] { NODE_FLAVOR, DataFlavor.stringFlavor }; |
| |
| /** |
| * The data being transfered. |
| */ |
| protected TransferData data; |
| |
| public TransferableTreeNode(TransferData data) { |
| this.data = data; |
| } |
| |
| public synchronized DataFlavor[] getTransferDataFlavors() { |
| return FLAVORS; |
| } |
| |
| /** |
| * Checks if the given date flavor is supported. |
| * |
| * @param flavor |
| * DataFlavor |
| * @return boolean |
| */ |
| public boolean isDataFlavorSupported(DataFlavor flavor) { |
| for (int i = 0; i < FLAVORS.length; i++) { |
| if (flavor.equals(FLAVORS[i])) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Data that is being transfered. |
| * |
| * @param flavor |
| * DataFlavor |
| * @return (TransferData data, String xmlString) |
| */ |
| public synchronized Object getTransferData(DataFlavor flavor) { |
| if (!isDataFlavorSupported(flavor)) { |
| return null; |
| } |
| if (flavor.equals(NODE_FLAVOR)) { |
| return data; |
| } else if (flavor.equals(DataFlavor.stringFlavor)) { |
| return data.getNodesAsXML(); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| /** |
| * The data being transfered on dnd. |
| */ |
| public static class TransferData { |
| |
| /** |
| * The nodes to transfer. |
| */ |
| protected ArrayList nodeList; |
| |
| /** |
| * Creates the TransferData. |
| * |
| * @param nodeList |
| * the nodeList |
| */ |
| public TransferData(ArrayList nodeList) { |
| this.nodeList = nodeList; |
| } |
| |
| /** |
| * Gets the nodeList. |
| * |
| * @return the nodeList |
| */ |
| public ArrayList getNodeList() { |
| return nodeList; |
| } |
| |
| /** |
| * Gets the concatenated string representation of the nodes in the node |
| * list. (To support string data flavor) |
| */ |
| public String getNodesAsXML() { |
| String toReturn = ""; |
| Iterator iterator = nodeList.iterator(); |
| while (iterator.hasNext()) { |
| Node node = (Node) iterator.next(); |
| toReturn += DOMUtilities.getXML(node); |
| } |
| return toReturn; |
| } |
| } |
| |
| // Autoscroll support |
| |
| public void autoscroll(Point point) { |
| JViewport viewport = |
| (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, |
| this); |
| if (viewport == null) { |
| return; |
| } |
| |
| Point viewportPos = viewport.getViewPosition(); |
| int viewHeight = viewport.getExtentSize().height; |
| int viewWidth = viewport.getExtentSize().width; |
| |
| // Scroll |
| if ((point.y - viewportPos.y) < autoscrollInsets.top) { |
| // Up |
| viewport.setViewPosition |
| (new Point(viewportPos.x, |
| Math.max(viewportPos.y - scrollUnits.top, 0))); |
| fireOnAutoscroll(new DOMDocumentTreeEvent(this)); |
| } else if ((viewportPos.y + viewHeight - point.y) |
| < autoscrollInsets.bottom) { |
| // Down |
| viewport.setViewPosition |
| (new Point(viewportPos.x, |
| Math.min(viewportPos.y + scrollUnits.bottom, |
| getHeight() - viewHeight))); |
| fireOnAutoscroll(new DOMDocumentTreeEvent(this)); |
| } else if ((point.x - viewportPos.x) < autoscrollInsets.left) { |
| // Left |
| viewport.setViewPosition |
| (new Point(Math.max(viewportPos.x - scrollUnits.left, 0), |
| viewportPos.y)); |
| fireOnAutoscroll(new DOMDocumentTreeEvent(this)); |
| } else if ((viewportPos.x + viewWidth - point.x) |
| < autoscrollInsets.right) { |
| // Right |
| viewport.setViewPosition |
| (new Point(Math.min(viewportPos.x + scrollUnits.right, |
| getWidth() - viewWidth), |
| viewportPos.y)); |
| fireOnAutoscroll(new DOMDocumentTreeEvent(this)); |
| } |
| } |
| |
| public Insets getAutoscrollInsets() { |
| int topAndBottom = getHeight(); |
| int leftAndRight = getWidth(); |
| return new Insets |
| (topAndBottom, leftAndRight, topAndBottom, leftAndRight); |
| } |
| |
| // Custom event support |
| |
| /** |
| * Event to pass to listener. |
| */ |
| public static class DOMDocumentTreeEvent extends EventObject { |
| |
| public DOMDocumentTreeEvent(Object source) { |
| super(source); |
| } |
| } |
| |
| /** |
| * The DOMDocumentTreeListener. |
| */ |
| public static interface DOMDocumentTreeListener extends EventListener { |
| |
| /** |
| * Fired after successfully completed drop. |
| * |
| * @param event |
| * the DOMDocumentTreeEvent |
| */ |
| void dropCompleted(DOMDocumentTreeEvent event); |
| |
| /** |
| * Fired when autoscroll is invoked |
| * |
| * @param event |
| * the DOMDocumentTreeEvent |
| */ |
| void onAutoscroll(DOMDocumentTreeEvent event); |
| } |
| |
| /** |
| * The adapter for the DOMDocumentTreeListener. |
| */ |
| public static class DOMDocumentTreeAdapter |
| implements DOMDocumentTreeListener { |
| |
| public void dropCompleted(DOMDocumentTreeEvent event) { |
| } |
| |
| public void onAutoscroll(DOMDocumentTreeEvent event) { |
| } |
| } |
| |
| /** |
| * Adds the listener to the listener list. |
| * |
| * @param listener |
| * The listener to add |
| */ |
| public void addListener(DOMDocumentTreeListener listener) { |
| eventListeners.add(DOMDocumentTreeListener.class, listener); |
| } |
| |
| /** |
| * Fires the dropCompleted event. |
| * |
| * @param event |
| * The associated DndTreeSupportEvent event |
| */ |
| public void fireDropCompleted(DOMDocumentTreeEvent event) { |
| Object[] listeners = eventListeners.getListenerList(); |
| int length = listeners.length; |
| for (int i = 0; i < length; i += 2) { |
| if (listeners[i] == DOMDocumentTreeListener.class) { |
| ((DOMDocumentTreeListener) listeners[i + 1]) |
| .dropCompleted(event); |
| } |
| } |
| } |
| |
| /** |
| * Fires the dropCompleted event. |
| * |
| * @param event |
| * The associated DndTreeSupportEvent event |
| */ |
| public void fireOnAutoscroll(DOMDocumentTreeEvent event) { |
| Object[] listeners = eventListeners.getListenerList(); |
| int length = listeners.length; |
| for (int i = 0; i < length; i += 2) { |
| if (listeners[i] == DOMDocumentTreeListener.class) { |
| ((DOMDocumentTreeListener) listeners[i + 1]) |
| .onAutoscroll(event); |
| } |
| } |
| } |
| |
| /** |
| * Contains the info for the 'dropCompleted' Event. |
| */ |
| public static class DropCompletedInfo { |
| |
| /** |
| * Parent node. |
| */ |
| protected Node parent; |
| |
| /** |
| * Nodes to be appended. |
| */ |
| protected ArrayList children; |
| |
| /** |
| * Next sibling node. |
| */ |
| protected Node sibling; |
| |
| /** |
| * @param parent |
| * Parent node |
| * @param children |
| * Nodes to be appended |
| */ |
| public DropCompletedInfo(Node parent, Node sibling, |
| ArrayList children) { |
| this.parent = parent; |
| this.sibling = sibling; |
| this.children = children; |
| } |
| |
| /** |
| * Gets the children. |
| * |
| * @return the children |
| */ |
| public ArrayList getChildren() { |
| return children; |
| } |
| |
| /** |
| * Getter for the parent. |
| * |
| * @return the parent |
| */ |
| public Node getParent() { |
| return parent; |
| } |
| |
| /** |
| * Getter for the sibling. |
| * |
| * @return the sibling |
| */ |
| public Node getSibling() { |
| return sibling; |
| } |
| } |
| |
| // Utility methods |
| |
| /** |
| * Gets the associated org.w3c.dom.Node from the DefaultMutableTreeNode |
| * |
| * @param treeNode |
| * The given DefaultMutableTreeNode |
| * @return the associated Node |
| */ |
| protected Node getDomNodeFromTreeNode(DefaultMutableTreeNode treeNode) { |
| if (treeNode == null) { |
| return null; |
| } |
| if (treeNode.getUserObject() instanceof NodeInfo) { |
| return ((NodeInfo) treeNode.getUserObject()).getNode(); |
| } |
| return null; |
| } |
| |
| /** |
| * Finds and returns a group of nodes that can be appended to the given |
| * parent node. |
| * |
| * @param potentialChildren |
| * The given potential children |
| * @param parentNode |
| * The given parent node |
| * @return list of nodes that can be appended to the given parent |
| */ |
| protected ArrayList getNodeListForParent(ArrayList potentialChildren, |
| Node parentNode) { |
| ArrayList children = new ArrayList(); |
| int n = potentialChildren.size(); |
| for (int i = 0; i < n; i++) { |
| Node node = (Node) potentialChildren.get(i); |
| if (DOMUtilities.canAppend(node, parentNode)) { |
| children.add(node); |
| } |
| } |
| return children; |
| } |
| } |