001: /*
002: * <copyright>
003: *
004: * Copyright 2000-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.tools.csmart.ui.tree;
028:
029: /**
030: * This is an example of a JTree, which serves as a DragSource as
031: * well as Drop Target.
032: * Adapted from Java Tutorial web site, under "Drag and Drop".
033: */
034:
035: import org.cougaar.tools.csmart.ui.viewer.CSMART;
036: import org.cougaar.util.log.Logger;
037:
038: import javax.swing.*;
039: import javax.swing.tree.DefaultMutableTreeNode;
040: import javax.swing.tree.DefaultTreeModel;
041: import javax.swing.tree.TreeNode;
042: import javax.swing.tree.TreePath;
043: import java.awt.*;
044: import java.awt.datatransfer.DataFlavor;
045: import java.awt.datatransfer.Transferable;
046: import java.awt.dnd.*;
047: import java.awt.event.InputEvent;
048: import java.awt.event.MouseEvent;
049: import java.awt.event.MouseListener;
050:
051: /**
052: * A JTree with drag and drop capability.
053: * The abstract methods need to be defined to provide
054: * drag and drop capability for a specific tree.
055: */
056:
057: public abstract class DNDTree extends JTree implements
058: DropTargetListener, DragSourceListener, DragGestureListener {
059: DropTarget dropTarget = null; // enables component to be a dropTarget
060: DragSource dragSource = null; // enables component to be a dragSource
061: DefaultMutableTreeNode[] dragSourceNodes; // The (TreeNodes) that are being dragged from here
062:
063: private transient Logger log;
064:
065: /**
066: * Initialize DropTarget and DragSource and JTree.
067: */
068:
069: public DNDTree(DefaultTreeModel model) {
070: super (model);
071: log = CSMART.createLogger(this .getClass().getName());
072: // make tree editable by default; drag checks the isEditable flag
073: setEditable(true);
074: setUI(new SpecialMetalTreeUI());
075: dropTarget = new DropTarget(this , this );
076: dragSource = new DragSource();
077: dragSource.createDefaultDragGestureRecognizer(this ,
078: DnDConstants.ACTION_COPY_OR_MOVE, this );
079: }
080:
081: private class SpecialMetalTreeUI extends
082: javax.swing.plaf.metal.MetalTreeUI {
083: public SpecialMetalTreeUI() {
084: super ();
085: }
086:
087: protected MouseListener createMouseListener() {
088: return new SpecialMouseHandler();
089: }
090:
091: public class SpecialMouseHandler extends MouseHandler {
092: MouseEvent pressEvent;
093:
094: public void mousePressed(MouseEvent e) {
095: pressEvent = e;
096: dragSourceNodes = null;
097: }
098:
099: public void mouseReleased(MouseEvent e) {
100: if (dragSourceNodes == null && pressEvent != null)
101: super .mousePressed(pressEvent);
102: }
103: }
104: }
105:
106: /** Utility to select a node in the tree **/
107: public void selectNode(TreeNode node) {
108: DefaultTreeModel model = (DefaultTreeModel) getModel();
109: TreePath path = new TreePath(model.getPathToRoot(node));
110: setSelectionPath(path);
111: }
112:
113: /** Utility to expand a node in the tree **/
114: public void expandNode(TreeNode node) {
115: DefaultTreeModel model = (DefaultTreeModel) getModel();
116: TreePath path = new TreePath(model.getPathToRoot(node));
117: expandPath(path);
118: }
119:
120: protected abstract int isDroppable(DataFlavor[] o,
121: DefaultMutableTreeNode target);
122:
123: protected abstract int addElement(Transferable o,
124: DefaultMutableTreeNode target, DefaultMutableTreeNode after);
125:
126: private DefaultMutableTreeNode getDropTarget(Point location) {
127: TreePath path = getPathForLocation(location.x, location.y);
128: if (path != null) {
129: return (DefaultMutableTreeNode) path.getLastPathComponent();
130: } else if (isRootVisible()) {
131: return null;
132: } else {
133: return (DefaultMutableTreeNode) ((DefaultTreeModel) getModel())
134: .getRoot();
135: }
136: }
137:
138: /**
139: * Return the action that is appropriate for this intended drop
140: **/
141: private int testDrop(DropTargetDragEvent event) {
142: DataFlavor[] possibleFlavors = event.getCurrentDataFlavors();
143: DefaultMutableTreeNode target = getDropTarget(event
144: .getLocation());
145: if (target != null) {
146: if (target.getAllowsChildren()) {
147: int action = isDroppable(possibleFlavors, target);
148: // if can't drop on the target, see if you can drop on its parent
149: if (action == DnDConstants.ACTION_NONE) {
150: target = (DefaultMutableTreeNode) target
151: .getParent();
152: if (target != null)
153: action = isDroppable(possibleFlavors, target);
154: return action;
155: }
156: return action;
157: } else {
158: // target doesn't allow children, but maybe can be sibling
159: target = (DefaultMutableTreeNode) target.getParent();
160: if (target.getAllowsChildren()) {
161: int action = isDroppable(possibleFlavors, target);
162: // if(log.isDebugEnabled()) {
163: // log.debug("Action is " + action);
164: // }
165: return action;
166: } else {
167: // if(log.isDebugEnabled()) {
168: // log.debug(target + " disallows children");
169: // }
170: }
171: }
172: } else {
173: // if(log.isDebugEnabled()) {
174: // log.debug("no target here");
175: // }
176: }
177: return DnDConstants.ACTION_NONE;
178: }
179:
180: /**
181: * Return true if the target allows this flavor.
182: */
183:
184: private boolean isAllowed(DataFlavor[] possibleFlavors,
185: DefaultMutableTreeNode target) {
186: if (target.getAllowsChildren()) {
187: int result = isDroppable(possibleFlavors, target);
188: return (result == DnDConstants.ACTION_MOVE || result == DnDConstants.ACTION_COPY);
189: } else
190: return false;
191: }
192:
193: /**
194: * A drop has occurred. Determine if the tree can accept the dropped item
195: * and if so, insert the item into the tree, otherwise reject the item.
196: */
197:
198: public void drop(DropTargetDropEvent event) {
199: int action = DnDConstants.ACTION_NONE;
200: DefaultMutableTreeNode target = getDropTarget(event
201: .getLocation());
202: DefaultMutableTreeNode after = null;
203: // allow drop if target allows children of this type
204: // or if an ancestor allows children of this type
205: DataFlavor[] possibleFlavors = event.getCurrentDataFlavors();
206: Transferable transferable = event.getTransferable();
207: while (target != null) {
208: if (isAllowed(possibleFlavors, target)) {
209: action = addElement(transferable, target, after);
210: break;
211: } else {
212: after = target;
213: target = (DefaultMutableTreeNode) after.getParent();
214: }
215: }
216: boolean success;
217: switch (action) {
218: case DnDConstants.ACTION_MOVE:
219: case DnDConstants.ACTION_COPY:
220: event.acceptDrop(action);
221: success = true;
222: break;
223: default:
224: event.rejectDrop();
225: success = false;
226: break;
227: }
228: event.getDropTargetContext().dropComplete(success);
229: }
230:
231: /**
232: * Decide what, if anything, should be dragged. If multi-drag is
233: * supported and the node under the mouse is in the current
234: * selection we attempt to drag the entire selection. If any node in
235: * the selection is not draggable we drag nothing. If multi-drag is
236: * not supported or if the node under the mouse is not in the
237: * current selection, we drag only the node under the mouse if it is
238: * draggable.
239: * If tree is not editable, return.
240: */
241:
242: public void dragGestureRecognized(DragGestureEvent event) {
243: InputEvent ie = event.getTriggerEvent();
244: // ignore right mouse events
245: if ((ie.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
246: return;
247: if (!isEditable())
248: return; // if tree isn't editable, return
249: DefaultMutableTreeNode target = getDropTarget(event
250: .getDragOrigin());
251: if (target == null)
252: return; // Nothing to drag.
253: TreePath[] paths = getSelectionPaths();
254: boolean doMultiDrag = paths != null && paths.length > 1
255: && supportsMultiDrag();
256: if (doMultiDrag) {
257: boolean targetIsSelected = false;
258: for (int i = 0; i < paths.length; i++) {
259: DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[i]
260: .getLastPathComponent();
261: if (!isDraggable(node)) {
262: doMultiDrag = false;
263: break;
264: }
265: if (node == target)
266: targetIsSelected = true;
267: }
268: if (!targetIsSelected)
269: doMultiDrag = false;
270: }
271: if (doMultiDrag) {
272: dragSourceNodes = new DefaultMutableTreeNode[paths.length];
273: for (int i = 0; i < paths.length; i++) {
274: dragSourceNodes[i] = (DefaultMutableTreeNode) paths[i]
275: .getLastPathComponent();
276: }
277: // if(log.isDebugEnabled()) {
278: // log.debug("Multi-drag " + dragSourceNodes.length + " nodes");
279: // }
280: Transferable draggableObject = null;
281: try {
282: draggableObject = makeDraggableObject(new DMTNArray(
283: dragSourceNodes));
284: } catch (IllegalArgumentException iae) {
285: if (log.isErrorEnabled()) {
286: log.error("Illegal argument exception: " + iae);
287: }
288: }
289: if (draggableObject == null)
290: return; // if couldn't make draggable object, then return
291: dragSource.startDrag(event, DragSource.DefaultMoveDrop,
292: draggableObject, this );
293: } else {
294: selectNode(target);
295: if (isDraggable(target)) {
296: dragSourceNodes = new DefaultMutableTreeNode[] { target };
297: Transferable draggableObject = null;
298: try {
299: draggableObject = makeDraggableObject(target);
300: } catch (IllegalArgumentException iae) {
301: if (log.isErrorEnabled()) {
302: log.error("Illegal argument exception: " + iae);
303: }
304: }
305: if (draggableObject == null)
306: return; // if couldn't make draggable object, then return
307: dragSource.startDrag(event, DragSource.DefaultMoveDrop,
308: draggableObject, this );
309: }
310: }
311: }
312:
313: protected boolean supportsMultiDrag() {
314: return false;
315: }
316:
317: public abstract boolean isDraggable(Object selected);
318:
319: /**
320: * DragSourceListener and DropTargetListener interface.
321: */
322:
323: /**
324: * Dragging has ended; remove dragged object from tree.
325: */
326:
327: public void dragDropEnd(DragSourceDropEvent event) {
328: // if(log.isDebugEnabled()) {
329: // log.debug("drop action = " + event.getDropAction());
330: // }
331: if (event.getDropSuccess()
332: && event.getDropAction() == DnDConstants.ACTION_MOVE)
333: removeElement();
334: }
335:
336: /**
337: * Removes a dragged element from this tree.
338: */
339:
340: public void removeElement() {
341: if (dragSourceNodes != null) {
342: for (int i = 0; i < dragSourceNodes.length; i++) {
343: ((DefaultTreeModel) getModel())
344: .removeNodeFromParent(dragSourceNodes[i]);
345: }
346: dragSourceNodes = null;
347: }
348: }
349:
350: /**
351: * This message goes to DragSourceListener, informing it that the dragging
352: * has entered the DropSite.
353: */
354:
355: public void dragEnter(DragSourceDragEvent event) {
356: // if(log.isDebugEnabled()) {
357: // log.debug( " drag source listener dragEnter");
358: // }
359: }
360:
361: /**
362: * This message goes to DragSourceListener, informing it that the dragging
363: * is currently ocurring over the DropSite.
364: */
365:
366: public void dragOver(DragSourceDragEvent event) {
367: // if(log.isDebugEnabled()) {
368: // log.debug( "dragExit");
369: // }
370: }
371:
372: /**
373: * This message goes to DragSourceListener, informing it that the dragging
374: * has exited the DropSite.
375: */
376:
377: public void dragExit(DragSourceEvent event) {
378: // if(log.isDebugEnabled()) {
379: // log.debug( "dragExit");
380: // }
381: }
382:
383: /**
384: * is invoked when the user changes the dropAction
385: *
386: */
387:
388: public void dropActionChanged(DragSourceDragEvent event) {
389: // if(log.isDebugEnabled()) {
390: // log.debug( "dropActionChanged");
391: // }
392: }
393:
394: /**
395: * Dragging over the DropSite.
396: */
397:
398: public void dragEnter(DropTargetDragEvent event) {
399: // start for debugging
400: // DefaultMutableTreeNode target = getDropTarget(event.getLocation());
401: // if (target != null)
402: // if(log.isDebugEnabled()) {
403: // log.debug("DRAG ENTER: " + target.getRoot());
404: // }
405: // else
406: // if(log.isDebugEnabled()) {
407: // log.debug("DRAG ENTER: " + null);
408: // }
409: // end for debugging
410: int action = testDrop(event);
411: if (action == DnDConstants.ACTION_NONE)
412: event.rejectDrag();
413: else
414: event.acceptDrag(DnDConstants.ACTION_MOVE);
415: }
416:
417: /**
418: * Drag operation is going on.
419: */
420:
421: public void dragOver(DropTargetDragEvent event) {
422: // start for debugging
423: // DefaultMutableTreeNode target = getDropTarget(event.getLocation());
424: // if (target != null)
425: // if(log.isDebugEnabled()) {
426: // log.debug("drag over: " + target.getRoot());
427: // }
428: // else
429: // if(log.isDebugEnabled()) {
430: // log.debug("drag over: " + null);
431: // }
432: // end for debugging
433: int action = testDrop(event);
434: if (action == DnDConstants.ACTION_NONE) {
435: event.rejectDrag();
436: } else
437: event.acceptDrag(DnDConstants.ACTION_MOVE);
438: }
439:
440: /**
441: * Exited DropSite without dropping.
442: */
443:
444: public void dragExit(DropTargetEvent event) {
445: // if(log.isDebugEnabled()) {
446: // log.debug( "dragExit");
447: // }
448: }
449:
450: /**
451: * User modifies the current drop gesture
452: */
453:
454: public void dropActionChanged(DropTargetDragEvent event) {
455: int action = testDrop(event);
456: if (action == DnDConstants.ACTION_NONE)
457: event.rejectDrag();
458: else
459: event.acceptDrag(DnDConstants.ACTION_MOVE);
460: }
461:
462: /**
463: * End DragSourceListener interface.
464: */
465:
466: /**
467: * A drag gesture has been initiated.
468: */
469:
470: public abstract Transferable makeDraggableObject(Object selected);
471:
472: }
|