/* * Copyright 2003-2010 Tufts University Licensed under the * Educational Community 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.osedu.org/licenses/ECL-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 tufts.vue; import tufts.Util; import java.awt.dnd.*; import java.awt.datatransfer.*; import java.awt.Dimension; import tufts.vue.MapDropTarget.DropHandler; import edu.tufts.vue.ontology.ui.TypeList; /** * implements java.awt.datatransfer.Transferable for LWComponent(s) * * @version $Revision: 1.5 $ / $Date: 2010-02-03 19:17:40 $ / $Author: mike $ * @author Scott Fraize */ public class LWTransfer implements Transferable { private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(LWTransfer.class); private static boolean isLocalDrop; // hack for working around java drop-handler bug that requests all byte representations no matter what public static void markAsLocal(boolean t) { isLocalDrop = t; } private static final DataFlavor DefaultFlavors[] = { LWComponent.DataFlavor, DataFlavor.stringFlavor, DataFlavor.imageFlavor, DropHandler.DataFlavor // TypeList.DataFlavor // commented out 2009-06-24 SMF //URLFlavor, // try text/uri-list }; private final LWComponent LWC; private final boolean isSelection; private final DataFlavor[] supportedFlavors; public LWTransfer(LWComponent c, boolean selection) { LWC = c; isSelection = selection; if (c.hasClientData(DropHandler.class)) { supportedFlavors = DefaultFlavors.clone(); } else { supportedFlavors = new DataFlavor[3]; System.arraycopy(DefaultFlavors, 0, supportedFlavors, 0, 3); } } public LWTransfer(LWComponent c) { this(c, false); } public DataFlavor[] getTransferDataFlavors() { return supportedFlavors; } public boolean isDataFlavorSupported(final DataFlavor flavor) { if (DEBUG.DND) Log.debug("isDataFlavorSupported, flavor=" + flavor); if (flavor == null) return false; if (flavor == LWComponent.DataFlavor && LWC instanceof LWMap) { // prevent the dropping of the entire map onto itself return false; } if (isLocalDrop && flavor == DataFlavor.imageFlavor) { // we never need raw image data when dropping onto a local source return false; } if (flavor == DropHandler.DataFlavor && LWC.hasClientData(DropHandler.class)) { return true; } // if (flavor == LWComponent.Producer.DataFlavor && LWC.hasClientData(LWComponent.Producer.class)) { // // note this is a bit convoluted: the producer is currently only passed within an // // LWComponent. This needn't be true, but our current usage depends on it. // // (We want an LWComponent available as the drag image). We could easily // // extend LWTransfer to support a producer directly and allowing for a null LWC. // return true; // } if (flavor == Resource.DataFlavor && LWC.getResource() == null) return false; // TODO BUG: support for TypeList is being incorrectly reported as true here // even if there is no old-style meta-data! for (int i = 0; i < supportedFlavors.length; i++) if (flavor.equals(supportedFlavors[i])) return true; return false; } public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, java.io.IOException { //tufts.Util.printStackTrace("GTD " + flavor.getHumanPresentableName()); // Note: the sun transfer handler (for drops to java) always requests // all data for all types during a drop, which is a horrible waste in // that we need to create a whole image every time, even it it's just // the resource being dropped. If want to optimize this out, would need // to create our own Image class that delays creation of the actual // image until something is requested of it. Drops to the OS, at // least in the case of MacOSX are smart and only request data for // what is ultimately dropped. // @see sun.awt.datatransfer.DataTransferer //Log.info("getTransferData " + Util.tags(flavor), new Throwable("HERE")); final String type = isLocalDrop ? "LOCAL " : "REMOTE "; Log.info("getTransferData " + type + flavor); if (DEBUG.DND && DEBUG.META) System.err.print("<LWTransfer.getTransferData(" + flavor.getHumanPresentableName() + ")>"); //if (DEBUG.DND && DEBUG.META) //Log.debug("getTransferData(" + flavor.getHumanPresentableName() + ")", new Throwable("HERE")); Object data = null; if (TypeList.DataFlavor.equals(flavor)) { try { data = LWC.getMetadataList().getMetadata().get(0).getObject(); } catch (Throwable t) { Log.warn("getTransferData: " + flavor + "; " + t); } } else if (DataFlavor.stringFlavor.equals(flavor)) { if (LWC != null && LWC.getClientData(DataFlavor.stringFlavor) != null) { return LWC.getClientData(DataFlavor.stringFlavor); } String s = null; if (LWC instanceof LWMap && ((LWMap)LWC).getFile() != null) s = ((LWMap)LWC).getFile().getAbsolutePath(); if (LWC.hasResource()) s = LWC.getResource().getSpec(); if (s == null && LWC.hasLabel()) s = LWC.getLabel(); if (s == null && LWC.hasNotes()) s = LWC.getNotes(); if (s == null) s = LWC.getDisplayLabel(); //if (s != null) s += "\n"; data = s; } else if (DataFlavor.imageFlavor.equals(flavor)) { //data = new LazyImage(new LazyImage.Producer() { // public Image getImage() { return LWC.getAsImage(); } //}); // The LazyImage method attempted above is no good for avoiding the creation of large // images unless they're needed. (1) sun.awt.image.SurfaceManager.getManager assumes // all images are BufferedImage's with a cast (!) And (2): The Java VM impl data // transfer code appears to request the byte representations of ALL THE SUPPORT FLAVORS // whenever a drop happens -- not just the flavor requested. (Which leads us to the // above cast failure, and this whole problem in the first place). E.g., this image // data should ONLY be requested if, say, we drop the image on the desktop or into // another app that will import the raw image data. But for some reason the impls // (including 1.5 & 1.6, at least Apple -- need to check Windows) request all possible // data, which can be a big issue if you happen to be dragging a very large image, but // are not interested in actually ever obtain all it's bytes. (E.g., very slow to copy // all those bytes, oftening hanging or even crashing the internal Apple code). if (isLocalDrop) { return null; //throw new java.io.IOException("raw image data never produced for local drops"); } else { return LWC.getAsImage(); } } else if (DropHandler.DataFlavor.equals(flavor)) { data = LWC.getClientData(DropHandler.class); //} else if (LWComponent.Producer.DataFlavor.equals(flavor)) { //data = LWC.getClientData(LWComponent.Producer.class); } else if (LWComponent.DataFlavor.equals(flavor)) { final java.util.Collection duplicates; if (isSelection) { duplicates = tufts.vue.Actions.duplicatePreservingLinks(LWC.getChildren()); } else if (LWC instanceof LWMap) { // don't send the actual map just yet... duplicates = tufts.vue.Actions.duplicatePreservingLinks(LWC.getChildren()); } // else if (LWC.hasClientData(LWComponent.Producer.class)) { // Util.printStackTrace("deprecated use of node producer"); // //duplicates = LWC.getClientData(LWComponent.ListFactory.class).produceNodes(null); // duplicates = LWC.getClientData(LWComponent.Producer.class).produceNodes(null); // } else if (LWC.hasFlag(LWComponent.Flag.INTERNAL) /*&& LWC.getClientProperty(Field.class) != null*/) { // an INTERNAL flagged LWComponent during drag is merely a bucket for other // information, and we can return it directly. return LWC; } else { duplicates = java.util.Collections.singletonList(LWC.duplicate()); } data = duplicates; } else if (Resource.DataFlavor.equals(flavor)) { data = LWC.getResource(); // } else if (URLFlavor.equals(flavor) && LWC.getResource() instanceof URLResource) { // data = ((URLResource)LWC.getResource()).asURL(); } else { throw new UnsupportedFlavorException(flavor); } //if (DEBUG.DND) out("LWTransfer: returning " + Util.tag(data)); return data; } }