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.viewer;
028:
029: import org.cougaar.tools.csmart.core.property.ModifiableComponent;
030: import org.cougaar.tools.csmart.experiment.DBExperiment;
031: import org.cougaar.tools.csmart.experiment.Experiment;
032: import org.cougaar.tools.csmart.recipe.RecipeComponent;
033: import org.cougaar.tools.csmart.society.SocietyComponent;
034: import org.cougaar.tools.csmart.ui.tree.CSMARTDataFlavor;
035: import org.cougaar.tools.csmart.ui.tree.ConsoleTreeObject;
036: import org.cougaar.tools.csmart.ui.tree.DMTNArray;
037: import org.cougaar.tools.csmart.ui.tree.DNDTree;
038: import org.cougaar.util.log.Logger;
039:
040: import javax.swing.tree.DefaultMutableTreeNode;
041: import javax.swing.tree.DefaultTreeModel;
042: import javax.swing.tree.TreeNode;
043: import javax.swing.tree.TreePath;
044: import java.awt.datatransfer.DataFlavor;
045: import java.awt.datatransfer.Transferable;
046: import java.awt.dnd.DnDConstants;
047:
048: public class OrganizerTree extends DNDTree {
049: private transient Logger log;
050:
051: public static final CSMARTDataFlavor folderFlavor = new CSMARTDataFlavor(
052: DefaultMutableTreeNode.class, DefaultMutableTreeNode.class,
053: OrganizerTree.class, "Folder");
054: public static final CSMARTDataFlavor societyFlavor = new CSMARTDataFlavor(
055: SocietyComponent.class, SocietyComponent.class,
056: OrganizerTree.class, "Society");
057: public static final CSMARTDataFlavor recipeFlavor = new CSMARTDataFlavor(
058: RecipeComponent.class, RecipeComponent.class,
059: OrganizerTree.class, "Recipe");
060: public static final CSMARTDataFlavor experimentFlavor = new CSMARTDataFlavor(
061: DBExperiment.class, DBExperiment.class,
062: OrganizerTree.class, "Experiment");
063: public static final CSMARTDataFlavor componentFlavor = new CSMARTDataFlavor(
064: ModifiableComponent.class, ModifiableComponent.class,
065: OrganizerTree.class, "Modifiable Component");
066:
067: private DefaultTreeModel model;
068:
069: /**
070: * MyTransferable encapsulates the DefaultMutableTreeNode
071: * being transferred.
072: */
073: private static class MyTransferable implements Transferable {
074: Object theData;
075: DataFlavor[] flavors;
076:
077: public MyTransferable(DefaultMutableTreeNode aNode) {
078: theData = aNode;
079: Object theUserData = aNode.getUserObject();
080: if (theUserData instanceof String)
081: flavors = new DataFlavor[] { folderFlavor };
082: else if (theUserData instanceof SocietyComponent)
083: flavors = new DataFlavor[] { societyFlavor };
084: else if (theUserData instanceof RecipeComponent)
085: flavors = new DataFlavor[] { recipeFlavor };
086: else if (theUserData instanceof Experiment)
087: flavors = new DataFlavor[] { experimentFlavor };
088: else if (theUserData instanceof ModifiableComponent)
089: flavors = new DataFlavor[] { componentFlavor };
090: else
091: throw new IllegalArgumentException("Unknown node");
092: }
093:
094: /**
095: * If the flavor allows children, then return
096: * the DefaultMutableTreeNode, so that the children can
097: * be transferred; otherwise just return the user object.
098: * @param flavor the flavor for the object being transferred
099: * @return the user object or a DefaultMutableTreeNode
100: */
101: public Object getTransferData(DataFlavor flavor) {
102: if (!flavor.equals(flavors[0]))
103: throw new IllegalArgumentException("Illegal DataFlavor");
104: if (flavor.equals(folderFlavor)
105: || flavor.equals(experimentFlavor))
106: return theData;
107: else
108: return ((DefaultMutableTreeNode) theData)
109: .getUserObject();
110: }
111:
112: public DataFlavor[] getTransferDataFlavors() {
113: return flavors;
114: }
115:
116: public boolean isDataFlavorSupported(DataFlavor flavor) {
117: return flavors[0].equals(flavor);
118: }
119: }
120:
121: /**
122: * Support for an array of nodes being transferred.
123: * Note this assumes that all nodes being dragged contain the
124: * same type of user object.
125: */
126: private static class MyArrayTransferable implements Transferable {
127: private DMTNArray nodes;
128: private CSMARTDataFlavor[] flavors;
129:
130: public MyArrayTransferable(DMTNArray nodes) {
131: this .nodes = nodes;
132: Object theUserData = nodes.nodes[0].getUserObject();
133: if (theUserData == null)
134: throw new IllegalArgumentException("null userObject");
135: if (theUserData instanceof String)
136: flavors = new CSMARTDataFlavor[] { folderFlavor };
137: else if (theUserData instanceof SocietyComponent)
138: flavors = new CSMARTDataFlavor[] { societyFlavor };
139: else if (theUserData instanceof RecipeComponent)
140: flavors = new CSMARTDataFlavor[] { recipeFlavor };
141: else if (theUserData instanceof Experiment)
142: flavors = new CSMARTDataFlavor[] { experimentFlavor };
143: else if (theUserData instanceof ModifiableComponent)
144: flavors = new CSMARTDataFlavor[] { componentFlavor };
145: else
146: throw new IllegalArgumentException("Unknown node");
147: }
148:
149: public synchronized Object getTransferData(DataFlavor flavor) {
150: if (flavor instanceof CSMARTDataFlavor) {
151: CSMARTDataFlavor cflavor = (CSMARTDataFlavor) flavor;
152: for (int i = 0; i < flavors.length; i++) {
153: if (ConsoleTreeObject.flavorEquals(cflavor,
154: flavors[i])) {
155: return nodes;
156: }
157: }
158: }
159: return null;
160: }
161:
162: public synchronized DataFlavor[] getTransferDataFlavors() {
163: return flavors;
164: }
165:
166: public boolean isDataFlavorSupported(DataFlavor flavor) {
167: return flavors[0].equals(flavor);
168: }
169: }
170:
171: /**
172: * Construct a tree from which objects can be dragged and dropped,
173: * and which is used to represent the workspace containing experiments,
174: * societies, recipes, etc.
175: * @param model the model for the tree
176: */
177: public OrganizerTree(DefaultTreeModel model) {
178: super (model);
179: this .model = model;
180: setExpandsSelectedPaths(true);
181: createLogger();
182: }
183:
184: private void createLogger() {
185: log = CSMART.createLogger(this .getClass().getName());
186: }
187:
188: /**
189: * Set the selection in the tree to be the specified tree node.
190: * @param treeNode the tree node to select
191: */
192: public void setSelection(TreeNode treeNode) {
193: TreeNode[] nodes = model.getPathToRoot(treeNode);
194: TreePath path = new TreePath(nodes);
195: setSelectionPath(path);
196: }
197:
198: /**
199: * Make a transferable object from the specified object.
200: * The specified object must be a <code>DefaultMutableTreeNode</code>.
201: * @param o the object to drag
202: */
203: public Transferable makeDraggableObject(Object o) {
204: if (o instanceof DefaultMutableTreeNode) {
205: DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
206: return new MyTransferable(node);
207: } else if (o instanceof DMTNArray) {
208: return new MyArrayTransferable((DMTNArray) o);
209: }
210: throw new IllegalArgumentException(
211: "Not a DefaultMutableTreeNode");
212: }
213:
214: /**
215: * Allows drop if target is either root or a folder;
216: * allows any source as long as it's from this tree.
217: * @param possibleFlavors possible flavors of the source
218: * @param target where the source will be dropped
219: */
220: public int isDroppable(DataFlavor[] possibleFlavors,
221: DefaultMutableTreeNode target) {
222: Object userObject = target.getUserObject();
223: // if dropping on root or a folder
224: if (userObject == null || userObject instanceof String) {
225: for (int i = 0; i < possibleFlavors.length; i++) {
226: DataFlavor flavor = possibleFlavors[i];
227: if (flavor instanceof CSMARTDataFlavor) {
228: CSMARTDataFlavor cflavor = (CSMARTDataFlavor) flavor;
229: // if dragging & dropping in this tree, then it's ok
230: if (getClass().getName().equals(
231: cflavor.getSourceClassName()))
232: return DnDConstants.ACTION_MOVE;
233: }
234: }
235: }
236: // dropping from another tree is not allowed
237: return DnDConstants.ACTION_NONE;
238: }
239:
240: /**
241: * The object is draggable if it's not root and it's not
242: * inside an experiment.
243: * @param o test if this object can be dragged
244: */
245: public boolean isDraggable(Object o) {
246: if (o instanceof DefaultMutableTreeNode) {
247: DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
248: if (node == getModel().getRoot())
249: return false; // not draggable if it's the root node
250: // not draggable if it's inside an experiment
251: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node
252: .getParent();
253: Object userObject = parentNode.getUserObject();
254: if (userObject != null && userObject instanceof Experiment)
255: return false;
256: return true;
257: }
258: return false;
259: }
260:
261: /**
262: * Add the dragged element to the drop site.
263: * If the dragged element has children, they are moved as well.
264: * If the before argument is null, then the dragged element
265: * is dropped at the end of the target's children.
266: * @param t the transferable for the dragged element
267: * @param target where to drop the element
268: * @param before optional, drop the element before this node in the target
269: */
270: public int addElement(Transferable t,
271: DefaultMutableTreeNode target, DefaultMutableTreeNode before) {
272: DataFlavor[] flavors = t.getTransferDataFlavors();
273: int action = isDroppable(flavors, target);
274: if (action == DnDConstants.ACTION_NONE)
275: return action; // do nothing
276: Object data = null;
277: try {
278: data = t.getTransferData(flavors[0]);
279: } catch (Exception e) {
280: if (log.isErrorEnabled()) {
281: log.error("Exception adding dropped element:", e);
282: return DnDConstants.ACTION_NONE;
283: }
284: }
285: if (data == null) {
286: if (log.isErrorEnabled()) {
287: log.error("Attempting to add null dropped element");
288: }
289: return DnDConstants.ACTION_NONE;
290: }
291: if (data instanceof DMTNArray) {
292: DMTNArray nodes = (DMTNArray) data;
293: for (int i = 0; i < nodes.nodes.length; i++)
294: addElement(nodes.nodes[i], target, before);
295: } else
296: addElement(data, target, before);
297: return action;
298: }
299:
300: private void addElement(Object data, DefaultMutableTreeNode target,
301: DefaultMutableTreeNode before) {
302: Object userData = data;
303: boolean allowsChildren = false;
304: // if data being added is a tree node, extract the user data
305: if (data instanceof DefaultMutableTreeNode)
306: userData = ((DefaultMutableTreeNode) data).getUserObject();
307: if (userData instanceof String
308: || userData instanceof Experiment)
309: allowsChildren = true;
310: // create a new tree node for the transferred data
311: DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
312: userData, allowsChildren);
313: int ix = target.getChildCount();
314: if (before != null) {
315: ix = model.getIndexOfChild(target, before);
316: }
317: if (log.isDebugEnabled()) {
318: log.debug("Insert into " + target + " at " + ix
319: + " before " + before);
320: }
321: // put the new tree node into the target
322: model.insertNodeInto(newNode, target, ix);
323: // add the children of the source, if any
324: if (data instanceof DefaultMutableTreeNode) {
325: DefaultMutableTreeNode source = (DefaultMutableTreeNode) data;
326: int n = source.getChildCount();
327: DefaultMutableTreeNode[] children = new DefaultMutableTreeNode[n];
328: for (int i = 0; i < n; i++)
329: children[i] = (DefaultMutableTreeNode) source
330: .getChildAt(i);
331: for (int i = 0; i < n; i++)
332: model.insertNodeInto(children[i], newNode, i);
333: }
334: // select the newly created node
335: selectNode(newNode);
336: }
337:
338: /**
339: * Overwrites DNDTree supportsMultiDrag to support multi-drag.
340: * @return boolean returns true
341: */
342: protected boolean supportsMultiDrag() {
343: return true;
344: }
345:
346: }
|