diff --git a/src/antidote/org/apache/tools/ant/gui/modules/edit/DragTree.java b/src/antidote/org/apache/tools/ant/gui/modules/edit/DragTree.java new file mode 100644 index 000000000..0e5e02759 --- /dev/null +++ b/src/antidote/org/apache/tools/ant/gui/modules/edit/DragTree.java @@ -0,0 +1,606 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999, 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.gui.modules.edit; + +import java.awt.datatransfer.*; +import java.awt.dnd.*; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Component; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreePath; +import javax.swing.JScrollPane; + +import java.io.IOException; +import java.util.TooManyListenersException; + +/** + * A tree which allows reorganization via drop and drag + * + * @version $Revision$ + * @author Nick Davisnick_home_account@yahoo.com + */ +public class DragTree extends JTree implements DragSourceListener, + DragGestureListener, DropTargetListener { + + /** + * The DragTreeListener + * associated with this DragTree. + */ + private DragTreeListener _dragTreeListener; + + /** + * Holds the position where the dropped item should be + * placed. Possible values are DROP_BEFORE, DROP_ON + * or DROP_AFTER. + */ + private int _dropPosition; + + /** + * The point where the drop line should be drawn. + */ + private Point _point; + + /** + * The object the drop occured on. + */ + private Object _dropOn; + + /** + * The path of where the drop occured. + */ + private TreePath _dropOnPath; + + /** + * The path of the item being dropped. + */ + private TreePath _droppedPath; + + /** + * The item being dragged should be placed before (or above) + * the item it is dropped on. + */ + final static protected int DROP_BEFORE = 0; + + /** + * The item being dragged should be placed on (as a child) + * the item it is dropped on. + */ + final static protected int DROP_ON = 1; + + /** + * The item being dragged should be placed on (or after) + * the item it is dropped on. + */ + final static protected int DROP_AFTER = 2; + + /** + * Default Constuctor + */ + public DragTree() { + + DragSource dragSource = DragSource.getDefaultDragSource(); + + // Use the default gesture recognizer + dragSource.createDefaultDragGestureRecognizer( + this, + DnDConstants.ACTION_COPY_OR_MOVE, + this); + + // Setup to be a drop target + new DropTarget(this, + DnDConstants.ACTION_COPY_OR_MOVE, + this); + } + + /** + * Starts the drag operation. + *

+ * @param e the DragGestureEvent describing + * the gesture that has just occurred + */ + public void dragGestureRecognized(DragGestureEvent e) { + + // Find the path for the cursor position. + Point p = e.getDragOrigin(); + _droppedPath = getPathForLocation(p.x, p.y); + + if (_droppedPath == null) { + return; + } + + // Select the item. + setSelectionPath(_droppedPath); + + // Wrap the object and start the drag. + Object obj = _droppedPath.getLastPathComponent(); + Wrapper wrapper = new Wrapper(obj); + e.startDrag(DragSource.DefaultMoveNoDrop, wrapper, this); + } + + // + // DragSourceListener methods + // + public void dragDropEnd(DragSourceDropEvent e) {} + public void dragEnter(DragSourceDragEvent e) {} + public void dragExit(DragSourceEvent e) {} + public void dragOver(DragSourceDragEvent e) {} + public void dropActionChanged(DragSourceDragEvent e) {} + + // + // DropTargetListener methods + // + public void dropActionChanged(java.awt.dnd.DropTargetDragEvent e) {} + public void dragEnter(DropTargetDragEvent e) { + dragOver(e); + } + + /** + * Called when a drag operation is ongoing + * on the DropTarget. + *

+ * @param dtde the DropTargetDragEvent + */ + public void dragOver(DropTargetDragEvent e) { + + checkAutoScroll(e.getLocation()); + + Point p = computeDropLocation(e.getLocation()); + + // Don't allow a parent to be dropped on one of its children. + if (_droppedPath.isDescendant(_dropOnPath)) { + _point = null; + _dropOn = null; + p = null; + e.rejectDrag(); + } else { + e.acceptDrag(e.getDropAction()); + } + + // If the point has changed, repaint the display. + if (_point == null || !p.equals(_point)) { + _point = p; + repaint(); + } + } + + /** + * Determines where the item will be dropped. + */ + private Point computeDropLocation(Point p) { + + int rowCount = getRowCount(); + int height = findCellHeight(); + int row = (p.y / height); + int offset = (p.y % height); + + // Move the point to the top of the cell. + p.y -= offset; + + int delta = 0; + + // Is the point at or past the end of the list? + if (row > (rowCount - 1) ) { + p.y = (rowCount - 1) * height; + row = rowCount - 1; + delta = height; + _dropPosition = DROP_AFTER; + } + // Is the point at the begining of the list? + else if (row <= 0) { + p.y = 0; + delta = height; + if (rowCount > 1) { + row = 1; + _dropPosition = DROP_BEFORE; + } else { + row = 0; + _dropPosition = DROP_ON; + } + } + // The point is in the middle of the tree. + else { + // Is the point on the top third of the cell? + if (offset < height * 0.333) { + + // Set the line to the top of the cell. + delta = 0; + _dropPosition = DROP_BEFORE; + } + // Is the point on the bottom third of the cell? + else if (offset > height * 0.666){ + + // Set the line to the bottom of the cell. + delta = height; + _dropPosition = DROP_AFTER; + } + // The point is in the middle of the cell? + else { + + // Set the line to the middle of the cell. + delta = height / 2; + _dropPosition = DROP_ON; + } + } + + // Find the object to use for the drop. + _dropOnPath = getPathForRow(row); + if (_dropOnPath != null) { + _dropOn = _dropOnPath.getLastPathComponent(); + } else { + _dropOn = null; + } + + // Adjust the point used to draw the drop line. + p.y += delta; + p.x = 0; + + return p; + } + + /** + * Process the drop + * + * @param e the DropTargetDropEvent + * @see DropTargetListener.drop + */ + public void drop(DropTargetDropEvent e) { + + if (_dropOn == null || _dropOnPath == null) { + return; + } + + // Get the object being transfered. + Object obj = null; + Transferable t = e.getTransferable(); + try { + obj = t.getTransferData(_flavors[0]); + } catch (Exception exp) { + System.out.println(exp); + } + + if (obj != null) { + + Object droppedObj = obj; + Object droppedOnObj = _dropOn; + Object parentObj = getParentOfDroppedOnObject(); + + if (_dropPosition == DROP_ON) { + + fireAppendChild(droppedOnObj, droppedObj); + setExpandedState(_dropOnPath, true); + } + else if (_dropPosition == DROP_BEFORE) { + + fireInsertBefore(parentObj, droppedOnObj, droppedObj); + } + else if (_dropPosition == DROP_AFTER) { + + // If the cell is exanded, add the new item before our + // first child. + if (isExpanded(getRowForPath(_dropOnPath))) { + parentObj = droppedOnObj; + } + + // Find the next sibling + int index = getModel().getIndexOfChild(parentObj, droppedOnObj); + int count = getModel().getChildCount(parentObj); + + if (index == count-1) { + fireAppendChild(parentObj, droppedObj); + } else { + Object sibling = getModel().getChild(parentObj, index+1); + fireInsertBefore(parentObj, sibling, droppedObj); + } + } + + if (e.getDropAction() == DnDConstants.ACTION_MOVE) { + fireRemoveChild(droppedObj); + } + + this.updateUI(); + } + } + + /** + * Returns the parent of the dropped on object. + */ + private Object getParentOfDroppedOnObject() { + int count = _dropOnPath.getPathCount(); + return _dropOnPath.getPathComponent(count-2); + } + + /** + * Repaint the display to cleanup any lines. + */ + public void dragExit(DropTargetEvent e) { + _point = null; + repaint(); + } + + /** + * Draws the line which shows where the dropped + * item will land. + * + * @see #JComponent.paintComponent + */ + public void paintComponent(Graphics g){ + super.paintComponent(g); + + if (_point != null) { + + if (_dropPosition == DROP_ON) { + // If the drop is on another item, draw + // two short lines. + g.drawLine(0, _point.y, 10, _point.y); + g.drawLine(getWidth()-10, _point.y, getWidth(), _point.y); + } else { + // If the drop is above or below an item, draw + // one long line. + g.drawLine(0, _point.y, getWidth(), _point.y); + } + } + } + + /** + * Wrapper holds the object to transfer + */ + protected class Wrapper implements Transferable { + + /** The object to transfer */ + private Object _obj; + + /** + * Creates a wrapper for the input object. + * + * @param obj object to wrap + */ + public Wrapper(Object obj) { + _obj = obj; + } + + /** + * Return out object if the DataFlavor is correct. + * + * @param flavor only javaJVMLocalObjectMimeType is supported + */ + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + if (isDataFlavorSupported(flavor)) { + return _obj; + } + throw new UnsupportedFlavorException(flavor); + } + + /** + * Return true if the input flavor is support. + * + * @param flavor DataFlavor to test + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(_flavors[0]); + } + + /** + * Return true if the input flavor is support. + * + * @param flavor DataFlavor to test + */ + public DataFlavor[] getTransferDataFlavors() { + return _flavors; + } + } + + /** + * DataFlavors which are support for transfer + */ + private static final DataFlavor[] _flavors = { + createConstant(DataFlavor.javaJVMLocalObjectMimeType) + }; + + /** + * Returns a new DataFlavor or null + * + * @param flavor the flavor + */ + static private DataFlavor createConstant(String flavor) { + try { + return new DataFlavor(flavor); + } catch (Exception e) { + return null; + } + } + + /** + * Returns the cell height for the tree. + */ + protected int findCellHeight() { + DefaultTreeCellRenderer renderer = + (DefaultTreeCellRenderer) getCellRenderer(); + return renderer.getPreferredSize().height; + } + + /** + * Register a new DragTreeListener. + *

+ * @param dtl the DragTreeListener to register + * with this DragTree. + */ + public synchronized void addDragTreeListener(DragTreeListener dtl) { + _dragTreeListener = dtl; + } + + /** + * unregister the current DragTreeListener + *

+ * @param dtl the DragTreeListener to unregister + *

+ * @throws IllegalArgumentException if + * dtl is not (equal to) the currently registered + * DragTreeListener. + */ + public synchronized void removeDragTreeListener(DragTreeListener dtl) { + if (_dragTreeListener == null || !_dragTreeListener.equals(dtl)) + throw new IllegalArgumentException(); + else { + _dragTreeListener = null; + } + } + + /** + * Notify the DragTreeListener that an appendChild has + * been requested. + */ + protected synchronized Object fireAppendChild(Object parent, + Object newChild) { + + if (_dragTreeListener != null) { + return _dragTreeListener.appendChild(parent, newChild); + } + return null; + } + + /** + * Notify the DragTreeListener that an insertBefore has + * been requested. + */ + protected synchronized Object fireInsertBefore(Object parent, Object index, + Object newChild) { + + if (_dragTreeListener != null) { + return _dragTreeListener.insertBefore(parent, index, newChild); + } + return null; + } + + /** + * Notify the DragTreeListener that an removeChild has + * been requested. + */ + protected synchronized void fireRemoveChild(Object child) { + + if (_dragTreeListener != null) { + _dragTreeListener.removeChild(child); + } + } + + /** + * Scrolls tree if nessasary + *

+ * @param p A Point indicating the + * location of the cursor that triggered this operation. + */ + protected void checkAutoScroll(Point p) { + + Point locn = new Point(p); + javax.swing.SwingUtilities.convertPointToScreen(locn, this); + javax.swing.SwingUtilities.convertPointFromScreen(locn, getParent()); + + Rectangle outer = new Rectangle(); + Rectangle inner = new Rectangle(); + + Insets i = new java.awt.Insets(10, 10, 10, 10); + Dimension size = getParent().getSize(); + + if (size.width != outer.width || size.height != outer.height) + outer.setBounds(0, 0, size.width, size.height); + + if (inner.x != i.left || inner.y != i.top) + inner.setLocation(i.left, i.top); + + int newWidth = size.width - (i.left + i.right); + int newHeight = size.height - (i.top + i.bottom); + + if (newWidth != inner.width || newHeight != inner.height) + inner.setSize(newWidth, newHeight); + + if (outer.contains(locn) && !inner.contains(locn)) { + if (locn.y >= inner.height) { + scrollDown(); + } else { + scrollUp(); + } + } + } + + /** + * Scroll the tree up one cell + */ + public void scrollUp() { + Rectangle visibleRect = getVisibleRect(); + int height = findCellHeight(); + visibleRect.y -= height; + visibleRect.height -= height; + scrollRectToVisible(visibleRect); + } + + /** + * Scroll the tree down one cell + */ + public void scrollDown() { + Rectangle visibleRect = getVisibleRect(); + int height = findCellHeight(); + visibleRect.y += height; + visibleRect.height += height; + scrollRectToVisible(visibleRect); + } +} diff --git a/src/antidote/org/apache/tools/ant/gui/modules/edit/DragTreeListener.java b/src/antidote/org/apache/tools/ant/gui/modules/edit/DragTreeListener.java new file mode 100644 index 000000000..cd2271dbb --- /dev/null +++ b/src/antidote/org/apache/tools/ant/gui/modules/edit/DragTreeListener.java @@ -0,0 +1,96 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999, 2000 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.gui.modules.edit; + +import java.util.EventListener; + +/** + * This interface is sourced from a DragTree and is invoked + * when an object of that (sub)class detects the completion + * of a drag operation. + * + * @version $Revision$ + * @author Nick Davisnick_home_account@yahoo.com + */ +public interface DragTreeListener extends EventListener { + + /** + * Append the object to the end of the parent's child list. + * + * @param parent parent object + * @param newChild new child object + * @return the new object + */ + public Object appendChild(Object parent, Object newChild); + + /** + * Append the object to the end of the parent's child list. + * + * @param parent parent object + * @param index child object of parent the newChild should be added + * before. + * @param newChild new child object + * @return the new object + */ + public Object insertBefore(Object parent, Object index, Object newChild); + + /** + * Removes the child object from the parent. + * + * @param parent parent object + * @param child child object + */ + public void removeChild(Object child); +}