- /*
- * @(#)DropTarget.java 1.48 03/12/19
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package java.awt.dnd;
-
- import java.util.TooManyListenersException;
-
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
-
- import java.awt.AWTEvent;
- import java.awt.Component;
- import java.awt.Dimension;
- import java.awt.GraphicsEnvironment;
- import java.awt.HeadlessException;
- import java.awt.Insets;
- import java.awt.Point;
- import java.awt.Rectangle;
- import java.awt.Toolkit;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.datatransfer.FlavorMap;
- import java.awt.datatransfer.SystemFlavorMap;
- import javax.swing.Timer;
- import java.awt.peer.ComponentPeer;
- import java.awt.peer.LightweightPeer;
- import java.awt.dnd.peer.DropTargetPeer;
-
-
- /**
- * The <code>DropTarget</code> is associated
- * with a <code>Component</code> when that <code>Component</code>
- * wishes
- * to accept drops during Drag and Drop operations.
- * <P>
- * Each
- * <code>DropTarget</code> is associated with a <code>FlavorMap</code>.
- * The default <code>FlavorMap</code> hereafter designates the
- * <code>FlavorMap</code> returned by <code>SystemFlavorMap.getDefaultFlavorMap()</code>.
- *
- * @version 1.48, 12/19/03
- * @since 1.2
- */
-
- public class DropTarget implements DropTargetListener, Serializable {
-
- private static final long serialVersionUID = -6283860791671019047L;
-
- /**
- * Creates a new DropTarget given the <code>Component</code>
- * to associate itself with, an <code>int</code> representing
- * the default acceptable action(s) to
- * support, a <code>DropTargetListener</code>
- * to handle event processing, a <code>boolean</code> indicating
- * if the <code>DropTarget</code> is currently accepting drops, and
- * a <code>FlavorMap</code> to use (or null for the default <CODE>FlavorMap</CODE>).
- * <P>
- * The Component will receive drops only if it is enabled.
- * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
- * @param ops The default acceptable actions for this <code>DropTarget</code>
- * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
- * @param act Is the <code>DropTarget</code> accepting drops.
- * @param fm The <code>FlavorMap</code> to use, or null for the default <CODE>FlavorMap</CODE>
- * @exception HeadlessException if GraphicsEnvironment.isHeadless()
- * returns true
- * @see java.awt.GraphicsEnvironment#isHeadless
- */
- public DropTarget(Component c, int ops, DropTargetListener dtl,
- boolean act, FlavorMap fm)
- throws HeadlessException
- {
- if (GraphicsEnvironment.isHeadless()) {
- throw new HeadlessException();
- }
-
- component = c;
-
- setDefaultActions(ops);
-
- if (dtl != null) try {
- addDropTargetListener(dtl);
- } catch (TooManyListenersException tmle) {
- // do nothing!
- }
-
- if (c != null) {
- c.setDropTarget(this);
- setActive(act);
- }
-
- if (fm != null) flavorMap = fm;
- }
-
- /**
- * Creates a <code>DropTarget</code> given the <code>Component</code>
- * to associate itself with, an <code>int</code> representing
- * the default acceptable action(s)
- * to support, a <code>DropTargetListener</code>
- * to handle event processing, and a <code>boolean</code> indicating
- * if the <code>DropTarget</code> is currently accepting drops.
- * <P>
- * The Component will receive drops only if it is enabled.
- * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
- * @param ops The default acceptable actions for this <code>DropTarget</code>
- * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
- * @param act Is the <code>DropTarget</code> accepting drops.
- * @exception HeadlessException if GraphicsEnvironment.isHeadless()
- * returns true
- * @see java.awt.GraphicsEnvironment#isHeadless
- */
- public DropTarget(Component c, int ops, DropTargetListener dtl,
- boolean act)
- throws HeadlessException
- {
- this(c, ops, dtl, act, null);
- }
-
- /**
- * Creates a <code>DropTarget</code>.
- * @exception HeadlessException if GraphicsEnvironment.isHeadless()
- * returns true
- * @see java.awt.GraphicsEnvironment#isHeadless
- */
- public DropTarget() throws HeadlessException {
- this(null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
- }
-
- /**
- * Creates a <code>DropTarget</code> given the <code>Component</code>
- * to associate itself with, and the <code>DropTargetListener</code>
- * to handle event processing.
- * <P>
- * The Component will receive drops only if it is enabled.
- * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
- * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
- * @exception HeadlessException if GraphicsEnvironment.isHeadless()
- * returns true
- * @see java.awt.GraphicsEnvironment#isHeadless
- */
- public DropTarget(Component c, DropTargetListener dtl)
- throws HeadlessException
- {
- this(c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
- }
-
- /**
- * Creates a <code>DropTarget</code> given the <code>Component</code>
- * to associate itself with, an <code>int</code> representing
- * the default acceptable action(s) to support, and a
- * <code>DropTargetListener</code> to handle event processing.
- * <P>
- * The Component will receive drops only if it is enabled.
- * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
- * @param ops The default acceptable actions for this <code>DropTarget</code>
- * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
- * @exception HeadlessException if GraphicsEnvironment.isHeadless()
- * returns true
- * @see java.awt.GraphicsEnvironment#isHeadless
- */
- public DropTarget(Component c, int ops, DropTargetListener dtl)
- throws HeadlessException
- {
- this(c, ops, dtl, true);
- }
-
- /**
- * Note: this interface is required to permit the safe association
- * of a DropTarget with a Component in one of two ways, either:
- * <code> component.setDropTarget(droptarget); </code>
- * or <code> droptarget.setComponent(component); </code>
- * <P>
- * The Component will receive drops only if it is enabled.
- * @param c The new <code>Component</code> this <code>DropTarget</code>
- * is to be associated with.<P>
- */
-
- public synchronized void setComponent(Component c) {
- if (component == c || component != null && component.equals(c))
- return;
-
- Component old;
- ComponentPeer oldPeer = null;
-
- if ((old = component) != null) {
- clearAutoscroll();
-
- component = null;
-
- if (componentPeer != null) {
- oldPeer = componentPeer;
- removeNotify(componentPeer);
- }
-
- old.setDropTarget(null);
-
- }
-
- if ((component = c) != null) try {
- c.setDropTarget(this);
- } catch (Exception e) { // undo the change
- if (old != null) {
- old.setDropTarget(this);
- addNotify(oldPeer);
- }
- }
- }
-
- /**
- * Gets the <code>Component</code> associated
- * with this <code>DropTarget</code>.
- * <P>
- * @return the current </code>Component</code>
- */
-
- public synchronized Component getComponent() {
- return component;
- }
-
- /**
- * Sets the default acceptable actions for this <code>DropTarget</code>
- * <P>
- * @param ops the default actions
- * <P>
- * @see java.awt.dnd.DnDConstants
- */
-
- public void setDefaultActions(int ops) {
- getDropTargetContext().setTargetActions(ops & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE));
- }
-
- /*
- * Called by DropTargetContext.setTargetActions()
- * with appropriate synchronization.
- */
- void doSetDefaultActions(int ops) {
- actions = ops;
- }
-
- /**
- * Gets an <code>int</code> representing the
- * current action(s) supported by this <code>DropTarget</code>.
- * <P>
- * @return the current default actions
- */
-
- public int getDefaultActions() {
- return actions;
- }
-
- /**
- * Sets the DropTarget active if <code>true</code>,
- * inactive if <code>false</code>.
- * <P>
- * @param isActive sets the <code>DropTarget</code> (in)active.
- */
-
- public synchronized void setActive(boolean isActive) {
- if (isActive != active) {
- active = isActive;
- }
-
- if (!active) clearAutoscroll();
- }
-
- /**
- * Reports whether or not
- * this <code>DropTarget</code>
- * is currently active (ready to accept drops).
- * <P>
- * @return <CODE>true</CODE> if active, <CODE>false</CODE> if not
- */
-
- public boolean isActive() {
- return active;
- }
-
- /**
- * Adds a new <code>DropTargetListener</code> (UNICAST SOURCE).
- * <P>
- * @param dtl The new <code>DropTargetListener</code>
- * <P>
- * @throws <code>TooManyListenersException</code> if a
- * <code>DropTargetListener</code> is already added to this
- * <code>DropTarget</code>.
- */
-
- public synchronized void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
- if (dtl == null) return;
-
- if (equals(dtl)) throw new IllegalArgumentException("DropTarget may not be its own Listener");
-
- if (dtListener == null)
- dtListener = dtl;
- else
- throw new TooManyListenersException();
- }
-
- /**
- * Removes the current <code>DropTargetListener</code> (UNICAST SOURCE).
- * <P>
- * @param dtl the DropTargetListener to deregister.
- */
-
- public synchronized void removeDropTargetListener(DropTargetListener dtl) {
- if (dtl != null && dtListener != null) {
- if(dtListener.equals(dtl))
- dtListener = null;
- else
- throw new IllegalArgumentException("listener mismatch");
- }
- }
-
- /**
- * Calls <code>dragEnter</code> on the registered
- * <code>DropTargetListener</code> and passes it
- * the specified <code>DropTargetDragEvent</code>.
- * Has no effect if this <code>DropTarget</code>
- * is not active.
- *
- * @param dtde the <code>DropTargetDragEvent</code>
- *
- * @throws NullPointerException if this <code>DropTarget</code>
- * is active and <code>dtde</code> is <code>null</code>
- *
- * @see #isActive
- */
- public synchronized void dragEnter(DropTargetDragEvent dtde) {
- if (!active) return;
-
- if (dtListener != null) {
- dtListener.dragEnter(dtde);
- } else
- dtde.getDropTargetContext().setTargetActions(DnDConstants.ACTION_NONE);
-
- initializeAutoscrolling(dtde.getLocation());
- }
-
- /**
- * Calls <code>dragOver</code> on the registered
- * <code>DropTargetListener</code> and passes it
- * the specified <code>DropTargetDragEvent</code>.
- * Has no effect if this <code>DropTarget</code>
- * is not active.
- *
- * @param dtde the <code>DropTargetDragEvent</code>
- *
- * @throws NullPointerException if this <code>DropTarget</code>
- * is active and <code>dtde</code> is <code>null</code>
- *
- * @see #isActive
- */
- public synchronized void dragOver(DropTargetDragEvent dtde) {
- if (!active) return;
-
- if (dtListener != null && active) dtListener.dragOver(dtde);
-
- updateAutoscroll(dtde.getLocation());
- }
-
- /**
- * Calls <code>dropActionChanged</code> on the registered
- * <code>DropTargetListener</code> and passes it
- * the specified <code>DropTargetDragEvent</code>.
- * Has no effect if this <code>DropTarget</code>
- * is not active.
- *
- * @param dtde the <code>DropTargetDragEvent</code>
- *
- * @throws NullPointerException if this <code>DropTarget</code>
- * is active and <code>dtde</code> is <code>null</code>
- *
- * @see #isActive
- */
- public synchronized void dropActionChanged(DropTargetDragEvent dtde) {
- if (!active) return;
-
- if (dtListener != null) dtListener.dropActionChanged(dtde);
-
- updateAutoscroll(dtde.getLocation());
- }
-
- /**
- * Calls <code>dragExit</code> on the registered
- * <code>DropTargetListener</code> and passes it
- * the specified <code>DropTargetEvent</code>.
- * Has no effect if this <code>DropTarget</code>
- * is not active.
- * <p>
- * This method itself does not throw any exception
- * for null parameter but for exceptions thrown by
- * the respective method of the listener.
- *
- * @param dte the <code>DropTargetEvent</code>
- *
- * @see #isActive
- */
- public synchronized void dragExit(DropTargetEvent dte) {
- if (!active) return;
-
- if (dtListener != null && active) dtListener.dragExit(dte);
-
- clearAutoscroll();
- }
-
- /**
- * Calls <code>drop</code> on the registered
- * <code>DropTargetListener</code> and passes it
- * the specified <code>DropTargetDropEvent</code>
- * if this <code>DropTarget</code> is active.
- *
- * @param dtde the <code>DropTargetDropEvent</code>
- *
- * @throws NullPointerException if <code>dtde</code> is null
- * and at least one of the following is true: this
- * <code>DropTarget</code> is not active, or there is
- * no a <code>DropTargetListener</code> registered.
- *
- * @see #isActive
- */
- public synchronized void drop(DropTargetDropEvent dtde) {
- clearAutoscroll();
-
- if (dtListener != null && active)
- dtListener.drop(dtde);
- else { // we should'nt get here ...
- dtde.rejectDrop();
- }
- }
-
- /**
- * Gets the <code>FlavorMap</code>
- * associated with this <code>DropTarget</code>.
- * If no <code>FlavorMap</code> has been set for this
- * <code>DropTarget</code>, it is associated with the default
- * <code>FlavorMap</code>.
- * <P>
- * @return the FlavorMap for this DropTarget
- */
-
- public FlavorMap getFlavorMap() { return flavorMap; }
-
- /**
- * Sets the <code>FlavorMap</code> associated
- * with this <code>DropTarget</code>.
- * <P>
- * @param fm the new <code>FlavorMap</code>, or null to
- * associate the default FlavorMap with this DropTarget.
- */
-
- public void setFlavorMap(FlavorMap fm) {
- flavorMap = fm == null ? SystemFlavorMap.getDefaultFlavorMap() : fm;
- }
-
- /**
- * Notify the DropTarget that it has been associated with a Component
- *
- **********************************************************************
- * This method is usually called from java.awt.Component.addNotify() of
- * the Component associated with this DropTarget to notify the DropTarget
- * that a ComponentPeer has been associated with that Component.
- *
- * Calling this method, other than to notify this DropTarget of the
- * association of the ComponentPeer with the Component may result in
- * a malfunction of the DnD system.
- **********************************************************************
- * <P>
- * @param peer The Peer of the Component we are associated with!
- *
- */
-
- public void addNotify(ComponentPeer peer) {
- if (peer == componentPeer) return;
-
- componentPeer = peer;
-
- for (Component c = component;
- c != null && peer instanceof LightweightPeer; c = c.getParent()) {
- peer = c.getPeer();
- }
-
- if (peer instanceof DropTargetPeer) {
- nativePeer = peer;
- ((DropTargetPeer)peer).addDropTarget(this);
- } else {
- nativePeer = null;
- }
- }
-
- /**
- * Notify the DropTarget that it has been disassociated from a Component
- *
- **********************************************************************
- * This method is usually called from java.awt.Component.removeNotify() of
- * the Component associated with this DropTarget to notify the DropTarget
- * that a ComponentPeer has been disassociated with that Component.
- *
- * Calling this method, other than to notify this DropTarget of the
- * disassociation of the ComponentPeer from the Component may result in
- * a malfunction of the DnD system.
- **********************************************************************
- * <P>
- * @param peer The Peer of the Component we are being disassociated from!
- */
-
- public void removeNotify(ComponentPeer peer) {
- if (nativePeer != null)
- ((DropTargetPeer)nativePeer).removeDropTarget(this);
-
- componentPeer = nativePeer = null;
- }
-
- /**
- * Gets the <code>DropTargetContext</code> associated
- * with this <code>DropTarget</code>.
- * <P>
- * @return the <code>DropTargetContext</code> associated with this <code>DropTarget</code>.
- */
-
- public DropTargetContext getDropTargetContext() {
- return dropTargetContext;
- }
-
- /**
- * Creates the DropTargetContext associated with this DropTarget.
- * Subclasses may override this method to instantiate their own
- * DropTargetContext subclass.
- *
- * This call is typically *only* called by the platform's
- * DropTargetContextPeer as a drag operation encounters this
- * DropTarget. Accessing the Context while no Drag is current
- * has undefined results.
- */
-
- protected DropTargetContext createDropTargetContext() {
- return new DropTargetContext(this);
- }
-
- /**
- * Serializes this <code>DropTarget</code>. Performs default serialization,
- * and then writes out this object's <code>DropTargetListener</code> if and
- * only if it can be serialized. If not, <code>null</code> is written
- * instead.
- *
- * @serialData The default serializable fields, in alphabetical order,
- * followed by either a <code>DropTargetListener</code>
- * instance, or <code>null</code>.
- * @since 1.4
- */
- private void writeObject(ObjectOutputStream s) throws IOException {
- s.defaultWriteObject();
-
- s.writeObject(SerializationTester.test(dtListener)
- ? dtListener : null);
- }
-
- /**
- * Deserializes this <code>DropTarget</code>. This method first performs
- * default deserialization for all non-<code>transient</code> fields. An
- * attempt is then made to deserialize this object's
- * <code>DropTargetListener</code> as well. This is first attempted by
- * deserializing the field <code>dtListener</code>, because, in releases
- * prior to 1.4, a non-<code>transient</code> field of this name stored the
- * <code>DropTargetListener</code>. If this fails, the next object in the
- * stream is used instead.
- *
- * @since 1.4
- */
- private void readObject(ObjectInputStream s)
- throws ClassNotFoundException, IOException
- {
- ObjectInputStream.GetField f = s.readFields();
-
- try {
- dropTargetContext =
- (DropTargetContext)f.get("dropTargetContext", null);
- } catch (IllegalArgumentException e) {
- // Pre-1.4 support. 'dropTargetContext' was previoulsy transient
- }
- if (dropTargetContext == null) {
- dropTargetContext = createDropTargetContext();
- }
-
- component = (Component)f.get("component", null);
- actions = f.get("actions", DnDConstants.ACTION_COPY_OR_MOVE);
- active = f.get("active", true);
-
- // Pre-1.4 support. 'dtListener' was previously non-transient
- try {
- dtListener = (DropTargetListener)f.get("dtListener", null);
- } catch (IllegalArgumentException e) {
- // 1.4-compatible byte stream. 'dtListener' was written explicitly
- dtListener = (DropTargetListener)s.readObject();
- }
- }
-
- /*********************************************************************/
-
- /**
- * this protected nested class implements autoscrolling
- */
-
- protected static class DropTargetAutoScroller implements ActionListener {
-
- /**
- * construct a DropTargetAutoScroller
- * <P>
- * @param c the <code>Component</code>
- * @param p the <code>Point</code>
- */
-
- protected DropTargetAutoScroller(Component c, Point p) {
- super();
-
- component = c;
- autoScroll = (Autoscroll)component;
-
- Toolkit t = Toolkit.getDefaultToolkit();
-
- Integer initial = new Integer(100);
- Integer interval = new Integer(100);
-
- try {
- initial = (Integer)t.getDesktopProperty("DnD.Autoscroll.initialDelay");
- } catch (Exception e) {
- // ignore
- }
-
- try {
- interval = (Integer)t.getDesktopProperty("DnD.Autoscroll.interval");
- } catch (Exception e) {
- // ignore
- }
-
- timer = new Timer(interval.intValue(), this);
-
- timer.setCoalesce(true);
- timer.setInitialDelay(initial.intValue());
-
- locn = p;
- prev = p;
-
- try {
- hysteresis = ((Integer)t.getDesktopProperty("DnD.Autoscroll.cursorHysteresis")).intValue();
- } catch (Exception e) {
- // ignore
- }
-
- timer.start();
- }
-
- /**
- * update the geometry of the autoscroll region
- */
-
- private void updateRegion() {
- Insets i = autoScroll.getAutoscrollInsets();
- Dimension size = component.getSize();
-
- if (size.width != outer.width || size.height != outer.height)
- outer.reshape(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);
-
- }
-
- /**
- * cause autoscroll to occur
- * <P>
- * @param newLocn the <code>Point</code>
- */
-
- protected synchronized void updateLocation(Point newLocn) {
- prev = locn;
- locn = newLocn;
-
- if (Math.abs(locn.x - prev.x) > hysteresis ||
- Math.abs(locn.y - prev.y) > hysteresis) {
- if (timer.isRunning()) timer.stop();
- } else {
- if (!timer.isRunning()) timer.start();
- }
- }
-
- /**
- * cause autoscrolling to stop
- */
-
- protected void stop() { timer.stop(); }
-
- /**
- * cause autoscroll to occur
- * <P>
- * @param e the <code>ActionEvent</code>
- */
-
- public synchronized void actionPerformed(ActionEvent e) {
- updateRegion();
-
- if (outer.contains(locn) && !inner.contains(locn))
- autoScroll.autoscroll(locn);
- }
-
- /*
- * fields
- */
-
- private Component component;
- private Autoscroll autoScroll;
-
- private Timer timer;
-
- private Point locn;
- private Point prev;
-
- private Rectangle outer = new Rectangle();
- private Rectangle inner = new Rectangle();
-
- private int hysteresis = 10;
- }
-
- /*********************************************************************/
-
- /**
- * create an embedded autoscroller
- * <P>
- * @param c the <code>Component</code>
- * @param p the <code>Point</code>
- */
-
- protected DropTargetAutoScroller createDropTargetAutoScroller(Component c, Point p) {
- return new DropTargetAutoScroller(c, p);
- }
-
- /**
- * initialize autoscrolling
- * <P>
- * @param p the <code>Point</code>
- */
-
- protected void initializeAutoscrolling(Point p) {
- if (component == null || !(component instanceof Autoscroll)) return;
-
- autoScroller = createDropTargetAutoScroller(component, p);
- }
-
- /**
- * update autoscrolling with current cursor locn
- * <P>
- * @param dragCursorLocn the <code>Point</code>
- */
-
- protected void updateAutoscroll(Point dragCursorLocn) {
- if (autoScroller != null) autoScroller.updateLocation(dragCursorLocn);
- }
-
- /**
- * clear autoscrolling
- */
-
- protected void clearAutoscroll() {
- if (autoScroller != null) {
- autoScroller.stop();
- autoScroller = null;
- }
- }
-
- /**
- * The DropTargetContext associated with this DropTarget.
- *
- * @serial
- */
- private DropTargetContext dropTargetContext = createDropTargetContext();
-
- /**
- * The Component associated with this DropTarget.
- *
- * @serial
- */
- private Component component;
-
- /*
- * That Component's Peer
- */
- private transient ComponentPeer componentPeer;
-
- /*
- * That Component's "native" Peer
- */
- private transient ComponentPeer nativePeer;
-
-
- /**
- * Default permissible actions supported by this DropTarget.
- *
- * @see #setDefaultActions
- * @see #getDefaultActions
- * @serial
- */
- int actions = DnDConstants.ACTION_COPY_OR_MOVE;
-
- /**
- * <code>true</code> if the DropTarget is accepting Drag & Drop operations.
- *
- * @serial
- */
- boolean active = true;
-
- /*
- * the auto scrolling object
- */
-
- private transient DropTargetAutoScroller autoScroller;
-
- /*
- * The delegate
- */
-
- private transient DropTargetListener dtListener;
-
- /*
- * The FlavorMap
- */
-
- private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap();
- }