/* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.awt.dnd; import java.awt.Component; import java.awt.Cursor; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.FlavorMap; import java.awt.datatransfer.SystemFlavorMap; import java.awt.datatransfer.Transferable; import java.awt.dnd.peer.DragSourceContextPeer; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.security.AccessController; import java.util.EventListener; import sun.awt.dnd.SunDragSourceContextPeer; import sun.security.action.GetIntegerAction; /** * The DragSource is the entity responsible * for the initiation of the Drag * and Drop operation, and may be used in a number of scenarios: * * * Once the DragSource is * obtained, a DragGestureRecognizer should * also be obtained to associate the DragSource * with a particular * Component. *

* The initial interpretation of the user's gesture, * and the subsequent starting of the drag operation * are the responsibility of the implementing * Component, which is usually * implemented by a DragGestureRecognizer. *

* When a drag gesture occurs, the * DragSource's * startDrag() method shall be * invoked in order to cause processing * of the user's navigational * gestures and delivery of Drag and Drop * protocol notifications. A * DragSource shall only * permit a single Drag and Drop operation to be * current at any one time, and shall * reject any further startDrag() requests * by throwing an IllegalDnDOperationException * until such time as the extant operation is complete. *

* The startDrag() method invokes the * createDragSourceContext() method to * instantiate an appropriate * DragSourceContext * and associate the DragSourceContextPeer * with that. *

* If the Drag and Drop System is * unable to initiate a drag operation for * some reason, the startDrag() method throws * a java.awt.dnd.InvalidDnDOperationException * to signal such a condition. Typically this * exception is thrown when the underlying platform * system is either not in a state to * initiate a drag, or the parameters specified are invalid. *

* Note that during the drag, the * set of operations exposed by the source * at the start of the drag operation may not change * until the operation is complete. * The operation(s) are constant for the * duration of the operation with respect to the * DragSource. * * @since 1.2 */ public class DragSource implements Serializable { private static final long serialVersionUID = 6236096958971414066L; /* * load a system default cursor */ private static Cursor load(String name) { if (GraphicsEnvironment.isHeadless()) { return null; } try { return (Cursor)Toolkit.getDefaultToolkit().getDesktopProperty(name); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("failed to load system cursor: " + name + " : " + e.getMessage()); } } /** * The default Cursor to use with a copy operation indicating * that a drop is currently allowed. null if * GraphicsEnvironment.isHeadless() returns true. * * @see java.awt.GraphicsEnvironment#isHeadless */ public static final Cursor DefaultCopyDrop = load("DnD.Cursor.CopyDrop"); /** * The default Cursor to use with a move operation indicating * that a drop is currently allowed. null if * GraphicsEnvironment.isHeadless() returns true. * * @see java.awt.GraphicsEnvironment#isHeadless */ public static final Cursor DefaultMoveDrop = load("DnD.Cursor.MoveDrop"); /** * The default Cursor to use with a link operation indicating * that a drop is currently allowed. null if * GraphicsEnvironment.isHeadless() returns true. * * @see java.awt.GraphicsEnvironment#isHeadless */ public static final Cursor DefaultLinkDrop = load("DnD.Cursor.LinkDrop"); /** * The default Cursor to use with a copy operation indicating * that a drop is currently not allowed. null if * GraphicsEnvironment.isHeadless() returns true. * * @see java.awt.GraphicsEnvironment#isHeadless */ public static final Cursor DefaultCopyNoDrop = load("DnD.Cursor.CopyNoDrop"); /** * The default Cursor to use with a move operation indicating * that a drop is currently not allowed. null if * GraphicsEnvironment.isHeadless() returns true. * * @see java.awt.GraphicsEnvironment#isHeadless */ public static final Cursor DefaultMoveNoDrop = load("DnD.Cursor.MoveNoDrop"); /** * The default Cursor to use with a link operation indicating * that a drop is currently not allowed. null if * GraphicsEnvironment.isHeadless() returns true. * * @see java.awt.GraphicsEnvironment#isHeadless */ public static final Cursor DefaultLinkNoDrop = load("DnD.Cursor.LinkNoDrop"); private static final DragSource dflt = (GraphicsEnvironment.isHeadless()) ? null : new DragSource(); /** * Internal constants for serialization. */ static final String dragSourceListenerK = "dragSourceL"; static final String dragSourceMotionListenerK = "dragSourceMotionL"; /** * Gets the DragSource object associated with * the underlying platform. * * @return the platform DragSource * @exception HeadlessException if GraphicsEnvironment.isHeadless() * returns true * @see java.awt.GraphicsEnvironment#isHeadless */ public static DragSource getDefaultDragSource() { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } else { return dflt; } } /** * Reports * whether or not drag * Image support * is available on the underlying platform. * * @return if the Drag Image support is available on this platform */ public static boolean isDragImageSupported() { Toolkit t = Toolkit.getDefaultToolkit(); Boolean supported; try { supported = (Boolean)Toolkit.getDefaultToolkit().getDesktopProperty("DnD.isDragImageSupported"); return supported.booleanValue(); } catch (Exception e) { return false; } } /** * Creates a new DragSource. * * @exception HeadlessException if GraphicsEnvironment.isHeadless() * returns true * @see java.awt.GraphicsEnvironment#isHeadless */ public DragSource() throws HeadlessException { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } } /** * Start a drag, given the DragGestureEvent * that initiated the drag, the initial * Cursor to use, * the Image to drag, * the offset of the Image origin * from the hotspot of the Cursor at * the instant of the trigger, * the Transferable subject data * of the drag, the DragSourceListener, * and the FlavorMap. * * @param trigger the DragGestureEvent that initiated the drag * @param dragCursor the initial {@code Cursor} for this drag operation * or {@code null} for the default cursor handling; * see DragSourceContext * for more details on the cursor handling mechanism during drag and drop * @param dragImage the image to drag or {@code null} * @param imageOffset the offset of the Image origin from the hotspot * of the Cursor at the instant of the trigger * @param transferable the subject data of the drag * @param dsl the DragSourceListener * @param flavorMap the FlavorMap to use, or null * * @throws java.awt.dnd.InvalidDnDOperationException * if the Drag and Drop * system is unable to initiate a drag operation, or if the user * attempts to start a drag while an existing drag operation * is still executing */ public void startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl, FlavorMap flavorMap) throws InvalidDnDOperationException { SunDragSourceContextPeer.setDragDropInProgress(true); try { if (flavorMap != null) this.flavorMap = flavorMap; DragSourceContextPeer dscp = Toolkit.getDefaultToolkit().createDragSourceContextPeer(trigger); DragSourceContext dsc = createDragSourceContext(dscp, trigger, dragCursor, dragImage, imageOffset, transferable, dsl ); if (dsc == null) { throw new InvalidDnDOperationException(); } dscp.startDrag(dsc, dsc.getCursor(), dragImage, imageOffset); // may throw } catch (RuntimeException e) { SunDragSourceContextPeer.setDragDropInProgress(false); throw e; } } /** * Start a drag, given the DragGestureEvent * that initiated the drag, the initial * Cursor to use, * the Transferable subject data * of the drag, the DragSourceListener, * and the FlavorMap. * * @param trigger the DragGestureEvent that * initiated the drag * @param dragCursor the initial {@code Cursor} for this drag operation * or {@code null} for the default cursor handling; * see DragSourceContext * for more details on the cursor handling mechanism during drag and drop * @param transferable the subject data of the drag * @param dsl the DragSourceListener * @param flavorMap the FlavorMap to use or null * * @throws java.awt.dnd.InvalidDnDOperationException * if the Drag and Drop * system is unable to initiate a drag operation, or if the user * attempts to start a drag while an existing drag operation * is still executing */ public void startDrag(DragGestureEvent trigger, Cursor dragCursor, Transferable transferable, DragSourceListener dsl, FlavorMap flavorMap) throws InvalidDnDOperationException { startDrag(trigger, dragCursor, null, null, transferable, dsl, flavorMap); } /** * Start a drag, given the DragGestureEvent * that initiated the drag, the initial Cursor * to use, * the Image to drag, * the offset of the Image origin * from the hotspot of the Cursor * at the instant of the trigger, * the subject data of the drag, and * the DragSourceListener. * * @param trigger the DragGestureEvent that initiated the drag * @param dragCursor the initial {@code Cursor} for this drag operation * or {@code null} for the default cursor handling; * see DragSourceContext * for more details on the cursor handling mechanism during drag and drop * @param dragImage the Image to drag or null * @param dragOffset the offset of the Image origin from the hotspot * of the Cursor at the instant of the trigger * @param transferable the subject data of the drag * @param dsl the DragSourceListener * * @throws java.awt.dnd.InvalidDnDOperationException * if the Drag and Drop * system is unable to initiate a drag operation, or if the user * attempts to start a drag while an existing drag operation * is still executing */ public void startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point dragOffset, Transferable transferable, DragSourceListener dsl) throws InvalidDnDOperationException { startDrag(trigger, dragCursor, dragImage, dragOffset, transferable, dsl, null); } /** * Start a drag, given the DragGestureEvent * that initiated the drag, the initial * Cursor to * use, * the Transferable subject data * of the drag, and the DragSourceListener. * * @param trigger the DragGestureEvent that initiated the drag * @param dragCursor the initial {@code Cursor} for this drag operation * or {@code null} for the default cursor handling; * see DragSourceContext class * for more details on the cursor handling mechanism during drag and drop * @param transferable the subject data of the drag * @param dsl the DragSourceListener * * @throws java.awt.dnd.InvalidDnDOperationException * if the Drag and Drop * system is unable to initiate a drag operation, or if the user * attempts to start a drag while an existing drag operation * is still executing */ public void startDrag(DragGestureEvent trigger, Cursor dragCursor, Transferable transferable, DragSourceListener dsl) throws InvalidDnDOperationException { startDrag(trigger, dragCursor, null, null, transferable, dsl, null); } /** * Creates the {@code DragSourceContext} to handle the current drag * operation. *

* To incorporate a new DragSourceContext * subclass, subclass DragSource and * override this method. *

* If dragImage is null, no image is used * to represent the drag over feedback for this drag operation, but * NullPointerException is not thrown. *

* If dsl is null, no drag source listener * is registered with the created DragSourceContext, * but NullPointerException is not thrown. * * @param dscp The DragSourceContextPeer for this drag * @param dgl The DragGestureEvent that triggered the * drag * @param dragCursor The initial {@code Cursor} for this drag operation * or {@code null} for the default cursor handling; * see DragSourceContext class * for more details on the cursor handling mechanism during drag and drop * @param dragImage The Image to drag or null * @param imageOffset The offset of the Image origin from the * hotspot of the cursor at the instant of the trigger * @param t The subject data of the drag * @param dsl The DragSourceListener * * @return the DragSourceContext * * @throws NullPointerException if dscp is null * @throws NullPointerException if dgl is null * @throws NullPointerException if dragImage is not * null and imageOffset is null * @throws NullPointerException if t is null * @throws IllegalArgumentException if the Component * associated with the trigger event is null. * @throws IllegalArgumentException if the DragSource for the * trigger event is null. * @throws IllegalArgumentException if the drag action for the * trigger event is DnDConstants.ACTION_NONE. * @throws IllegalArgumentException if the source actions for the * DragGestureRecognizer associated with the trigger * event are equal to DnDConstants.ACTION_NONE. */ protected DragSourceContext createDragSourceContext(DragSourceContextPeer dscp, DragGestureEvent dgl, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable t, DragSourceListener dsl) { return new DragSourceContext(dscp, dgl, dragCursor, dragImage, imageOffset, t, dsl); } /** * This method returns the * FlavorMap for this DragSource. * * @return the FlavorMap for this DragSource */ public FlavorMap getFlavorMap() { return flavorMap; } /** * Creates a new DragGestureRecognizer * that implements the specified * abstract subclass of * DragGestureRecognizer, and * sets the specified Component * and DragGestureListener on * the newly created object. * * @param the type of {@code DragGestureRecognizer} to create * @param recognizerAbstractClass the requested abstract type * @param actions the permitted source drag actions * @param c the Component target * @param dgl the DragGestureListener to notify * * @return the new DragGestureRecognizer or null * if the Toolkit.createDragGestureRecognizer method * has no implementation available for * the requested DragGestureRecognizer * subclass and returns null */ public T createDragGestureRecognizer(Class recognizerAbstractClass, Component c, int actions, DragGestureListener dgl) { return Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizerAbstractClass, this, c, actions, dgl); } /** * Creates a new DragGestureRecognizer * that implements the default * abstract subclass of DragGestureRecognizer * for this DragSource, * and sets the specified Component * and DragGestureListener on the * newly created object. * * For this DragSource * the default is MouseDragGestureRecognizer. * * @param c the Component target for the recognizer * @param actions the permitted source actions * @param dgl the DragGestureListener to notify * * @return the new DragGestureRecognizer or null * if the Toolkit.createDragGestureRecognizer method * has no implementation available for * the requested DragGestureRecognizer * subclass and returns null */ public DragGestureRecognizer createDefaultDragGestureRecognizer(Component c, int actions, DragGestureListener dgl) { return Toolkit.getDefaultToolkit().createDragGestureRecognizer(MouseDragGestureRecognizer.class, this, c, actions, dgl); } /** * Adds the specified DragSourceListener to this * DragSource to receive drag source events during drag * operations initiated with this DragSource. * If a null listener is specified, no action is taken and no * exception is thrown. * * @param dsl the DragSourceListener to add * * @see #removeDragSourceListener * @see #getDragSourceListeners * @since 1.4 */ public void addDragSourceListener(DragSourceListener dsl) { if (dsl != null) { synchronized (this) { listener = DnDEventMulticaster.add(listener, dsl); } } } /** * Removes the specified DragSourceListener from this * DragSource. * If a null listener is specified, no action is taken and no * exception is thrown. * If the listener specified by the argument was not previously added to * this DragSource, no action is taken and no exception * is thrown. * * @param dsl the DragSourceListener to remove * * @see #addDragSourceListener * @see #getDragSourceListeners * @since 1.4 */ public void removeDragSourceListener(DragSourceListener dsl) { if (dsl != null) { synchronized (this) { listener = DnDEventMulticaster.remove(listener, dsl); } } } /** * Gets all the DragSourceListeners * registered with this DragSource. * * @return all of this DragSource's * DragSourceListeners or an empty array if no * such listeners are currently registered * * @see #addDragSourceListener * @see #removeDragSourceListener * @since 1.4 */ public DragSourceListener[] getDragSourceListeners() { return getListeners(DragSourceListener.class); } /** * Adds the specified DragSourceMotionListener to this * DragSource to receive drag motion events during drag * operations initiated with this DragSource. * If a null listener is specified, no action is taken and no * exception is thrown. * * @param dsml the DragSourceMotionListener to add * * @see #removeDragSourceMotionListener * @see #getDragSourceMotionListeners * @since 1.4 */ public void addDragSourceMotionListener(DragSourceMotionListener dsml) { if (dsml != null) { synchronized (this) { motionListener = DnDEventMulticaster.add(motionListener, dsml); } } } /** * Removes the specified DragSourceMotionListener from this * DragSource. * If a null listener is specified, no action is taken and no * exception is thrown. * If the listener specified by the argument was not previously added to * this DragSource, no action is taken and no exception * is thrown. * * @param dsml the DragSourceMotionListener to remove * * @see #addDragSourceMotionListener * @see #getDragSourceMotionListeners * @since 1.4 */ public void removeDragSourceMotionListener(DragSourceMotionListener dsml) { if (dsml != null) { synchronized (this) { motionListener = DnDEventMulticaster.remove(motionListener, dsml); } } } /** * Gets all of the DragSourceMotionListeners * registered with this DragSource. * * @return all of this DragSource's * DragSourceMotionListeners or an empty array if no * such listeners are currently registered * * @see #addDragSourceMotionListener * @see #removeDragSourceMotionListener * @since 1.4 */ public DragSourceMotionListener[] getDragSourceMotionListeners() { return getListeners(DragSourceMotionListener.class); } /** * Gets all the objects currently registered as * FooListeners upon this DragSource. * FooListeners are registered using the * addFooListener method. * * @param the type of listener objects * @param listenerType the type of listeners requested; this parameter * should specify an interface that descends from * java.util.EventListener * @return an array of all objects registered as * FooListeners on this * DragSource, or an empty array if no such listeners * have been added * @exception ClassCastException if listenerType * doesn't specify a class or interface that implements * java.util.EventListener * * @see #getDragSourceListeners * @see #getDragSourceMotionListeners * @since 1.4 */ public T[] getListeners(Class listenerType) { EventListener l = null; if (listenerType == DragSourceListener.class) { l = listener; } else if (listenerType == DragSourceMotionListener.class) { l = motionListener; } return DnDEventMulticaster.getListeners(l, listenerType); } /** * This method calls dragEnter on the * DragSourceListeners registered with this * DragSource, and passes them the specified * DragSourceDragEvent. * * @param dsde the DragSourceDragEvent */ void processDragEnter(DragSourceDragEvent dsde) { DragSourceListener dsl = listener; if (dsl != null) { dsl.dragEnter(dsde); } } /** * This method calls dragOver on the * DragSourceListeners registered with this * DragSource, and passes them the specified * DragSourceDragEvent. * * @param dsde the DragSourceDragEvent */ void processDragOver(DragSourceDragEvent dsde) { DragSourceListener dsl = listener; if (dsl != null) { dsl.dragOver(dsde); } } /** * This method calls dropActionChanged on the * DragSourceListeners registered with this * DragSource, and passes them the specified * DragSourceDragEvent. * * @param dsde the DragSourceDragEvent */ void processDropActionChanged(DragSourceDragEvent dsde) { DragSourceListener dsl = listener; if (dsl != null) { dsl.dropActionChanged(dsde); } } /** * This method calls dragExit on the * DragSourceListeners registered with this * DragSource, and passes them the specified * DragSourceEvent. * * @param dse the DragSourceEvent */ void processDragExit(DragSourceEvent dse) { DragSourceListener dsl = listener; if (dsl != null) { dsl.dragExit(dse); } } /** * This method calls dragDropEnd on the * DragSourceListeners registered with this * DragSource, and passes them the specified * DragSourceDropEvent. * * @param dsde the DragSourceEvent */ void processDragDropEnd(DragSourceDropEvent dsde) { DragSourceListener dsl = listener; if (dsl != null) { dsl.dragDropEnd(dsde); } } /** * This method calls dragMouseMoved on the * DragSourceMotionListeners registered with this * DragSource, and passes them the specified * DragSourceDragEvent. * * @param dsde the DragSourceEvent */ void processDragMouseMoved(DragSourceDragEvent dsde) { DragSourceMotionListener dsml = motionListener; if (dsml != null) { dsml.dragMouseMoved(dsde); } } /** * Serializes this DragSource. This method first performs * default serialization. Next, it writes out this object's * FlavorMap if and only if it can be serialized. If not, * null is written instead. Next, it writes out * Serializable listeners registered with this * object. Listeners are written in a null-terminated sequence * of 0 or more pairs. The pair consists of a String and an * Object; the String indicates the type of the * Object and is one of the following: *

* * @serialData Either a FlavorMap instance, or * null, followed by a null-terminated * sequence of 0 or more pairs; the pair consists of a * String and an Object; the * String indicates the type of the Object * and is one of the following: * . * @since 1.4 */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); s.writeObject(SerializationTester.test(flavorMap) ? flavorMap : null); DnDEventMulticaster.save(s, dragSourceListenerK, listener); DnDEventMulticaster.save(s, dragSourceMotionListenerK, motionListener); s.writeObject(null); } /** * Deserializes this DragSource. This method first performs * default deserialization. Next, this object's FlavorMap is * deserialized by using the next object in the stream. * If the resulting FlavorMap is null, this * object's FlavorMap is set to the default FlavorMap for * this thread's ClassLoader. * Next, this object's listeners are deserialized by reading a * null-terminated sequence of 0 or more key/value pairs * from the stream: * * * @see java.awt.datatransfer.SystemFlavorMap#getDefaultFlavorMap * @since 1.4 */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); // 'flavorMap' was written explicitly flavorMap = (FlavorMap)s.readObject(); // Implementation assumes 'flavorMap' is never null. if (flavorMap == null) { flavorMap = SystemFlavorMap.getDefaultFlavorMap(); } Object keyOrNull; while (null != (keyOrNull = s.readObject())) { String key = ((String)keyOrNull).intern(); if (dragSourceListenerK == key) { addDragSourceListener((DragSourceListener)(s.readObject())); } else if (dragSourceMotionListenerK == key) { addDragSourceMotionListener( (DragSourceMotionListener)(s.readObject())); } else { // skip value for unrecognized key s.readObject(); } } } /** * Returns the drag gesture motion threshold. The drag gesture motion threshold * defines the recommended behavior for {@link MouseDragGestureRecognizer}s. *

* If the system property awt.dnd.drag.threshold is set to * a positive integer, this method returns the value of the system property; * otherwise if a pertinent desktop property is available and supported by * the implementation of the Java platform, this method returns the value of * that property; otherwise this method returns some default value. * The pertinent desktop property can be queried using * java.awt.Toolkit.getDesktopProperty("DnD.gestureMotionThreshold"). * * @return the drag gesture motion threshold * @see MouseDragGestureRecognizer * @since 1.5 */ public static int getDragThreshold() { int ts = AccessController.doPrivileged( new GetIntegerAction("awt.dnd.drag.threshold", 0)).intValue(); if (ts > 0) { return ts; } else { Integer td = (Integer)Toolkit.getDefaultToolkit(). getDesktopProperty("DnD.gestureMotionThreshold"); if (td != null) { return td.intValue(); } } return 5; } /* * fields */ private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap(); private transient DragSourceListener listener; private transient DragSourceMotionListener motionListener; }