1 /*
   2  * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt.dnd;
  27 
  28 import java.util.TooManyListenersException;
  29 
  30 import java.io.IOException;
  31 import java.io.ObjectInputStream;
  32 import java.io.ObjectOutputStream;
  33 import java.io.Serializable;
  34 
  35 import java.awt.Component;
  36 import java.awt.Dimension;
  37 import java.awt.GraphicsEnvironment;
  38 import java.awt.HeadlessException;
  39 import java.awt.Insets;
  40 import java.awt.Point;
  41 import java.awt.Rectangle;
  42 import java.awt.Toolkit;
  43 import java.awt.event.ActionEvent;
  44 import java.awt.event.ActionListener;
  45 import java.awt.datatransfer.FlavorMap;
  46 import java.awt.datatransfer.SystemFlavorMap;
  47 import javax.swing.Timer;
  48 import java.awt.peer.ComponentPeer;
  49 import java.awt.peer.LightweightPeer;
  50 import java.awt.dnd.peer.DropTargetPeer;
  51 
  52 
  53 /**
  54  * The <code>DropTarget</code> is associated
  55  * with a <code>Component</code> when that <code>Component</code>
  56  * wishes
  57  * to accept drops during Drag and Drop operations.
  58  * <P>
  59  *  Each
  60  * <code>DropTarget</code> is associated with a <code>FlavorMap</code>.
  61  * The default <code>FlavorMap</code> hereafter designates the
  62  * <code>FlavorMap</code> returned by <code>SystemFlavorMap.getDefaultFlavorMap()</code>.
  63  *
  64  * @since 1.2
  65  */
  66 
  67 public class DropTarget implements DropTargetListener, Serializable {
  68 
  69     private static final long serialVersionUID = -6283860791671019047L;
  70 
  71     /**
  72      * Creates a new DropTarget given the <code>Component</code>
  73      * to associate itself with, an <code>int</code> representing
  74      * the default acceptable action(s) to
  75      * support, a <code>DropTargetListener</code>
  76      * to handle event processing, a <code>boolean</code> indicating
  77      * if the <code>DropTarget</code> is currently accepting drops, and
  78      * a <code>FlavorMap</code> to use (or null for the default <CODE>FlavorMap</CODE>).
  79      * <P>
  80      * The Component will receive drops only if it is enabled.
  81      * @param c         The <code>Component</code> with which this <code>DropTarget</code> is associated
  82      * @param ops       The default acceptable actions for this <code>DropTarget</code>
  83      * @param dtl       The <code>DropTargetListener</code> for this <code>DropTarget</code>
  84      * @param act       Is the <code>DropTarget</code> accepting drops.
  85      * @param fm        The <code>FlavorMap</code> to use, or null for the default <CODE>FlavorMap</CODE>
  86      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  87      *            returns true
  88      * @see java.awt.GraphicsEnvironment#isHeadless
  89      */
  90     public DropTarget(Component c, int ops, DropTargetListener dtl,
  91                       boolean act, FlavorMap fm)
  92         throws HeadlessException
  93     {
  94         if (GraphicsEnvironment.isHeadless()) {
  95             throw new HeadlessException();
  96         }
  97 
  98         component = c;
  99 
 100         setDefaultActions(ops);
 101 
 102         if (dtl != null) try {
 103             addDropTargetListener(dtl);
 104         } catch (TooManyListenersException tmle) {
 105             // do nothing!
 106         }
 107 
 108         if (c != null) {
 109             c.setDropTarget(this);
 110             setActive(act);
 111         }
 112 
 113         if (fm != null) {
 114             flavorMap = fm;
 115         } else {
 116             flavorMap = SystemFlavorMap.getDefaultFlavorMap();
 117         }
 118     }
 119 
 120     /**
 121      * Creates a <code>DropTarget</code> given the <code>Component</code>
 122      * to associate itself with, an <code>int</code> representing
 123      * the default acceptable action(s)
 124      * to support, a <code>DropTargetListener</code>
 125      * to handle event processing, and a <code>boolean</code> indicating
 126      * if the <code>DropTarget</code> is currently accepting drops.
 127      * <P>
 128      * The Component will receive drops only if it is enabled.
 129      * @param c         The <code>Component</code> with which this <code>DropTarget</code> is associated
 130      * @param ops       The default acceptable actions for this <code>DropTarget</code>
 131      * @param dtl       The <code>DropTargetListener</code> for this <code>DropTarget</code>
 132      * @param act       Is the <code>DropTarget</code> accepting drops.
 133      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 134      *            returns true
 135      * @see java.awt.GraphicsEnvironment#isHeadless
 136      */
 137     public DropTarget(Component c, int ops, DropTargetListener dtl,
 138                       boolean act)
 139         throws HeadlessException
 140     {
 141         this(c, ops, dtl, act, null);
 142     }
 143 
 144     /**
 145      * Creates a <code>DropTarget</code>.
 146      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 147      *            returns true
 148      * @see java.awt.GraphicsEnvironment#isHeadless
 149      */
 150     public DropTarget() throws HeadlessException {
 151         this(null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
 152     }
 153 
 154     /**
 155      * Creates a <code>DropTarget</code> given the <code>Component</code>
 156      * to associate itself with, and the <code>DropTargetListener</code>
 157      * to handle event processing.
 158      * <P>
 159      * The Component will receive drops only if it is enabled.
 160      * @param c         The <code>Component</code> with which this <code>DropTarget</code> is associated
 161      * @param dtl       The <code>DropTargetListener</code> for this <code>DropTarget</code>
 162      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 163      *            returns true
 164      * @see java.awt.GraphicsEnvironment#isHeadless
 165      */
 166     public DropTarget(Component c, DropTargetListener dtl)
 167         throws HeadlessException
 168     {
 169         this(c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
 170     }
 171 
 172     /**
 173      * Creates a <code>DropTarget</code> given the <code>Component</code>
 174      * to associate itself with, an <code>int</code> representing
 175      * the default acceptable action(s) to support, and a
 176      * <code>DropTargetListener</code> to handle event processing.
 177      * <P>
 178      * The Component will receive drops only if it is enabled.
 179      * @param c         The <code>Component</code> with which this <code>DropTarget</code> is associated
 180      * @param ops       The default acceptable actions for this <code>DropTarget</code>
 181      * @param dtl       The <code>DropTargetListener</code> for this <code>DropTarget</code>
 182      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 183      *            returns true
 184      * @see java.awt.GraphicsEnvironment#isHeadless
 185      */
 186     public DropTarget(Component c, int ops, DropTargetListener dtl)
 187         throws HeadlessException
 188     {
 189         this(c, ops, dtl, true);
 190     }
 191 
 192     /**
 193      * Note: this interface is required to permit the safe association
 194      * of a DropTarget with a Component in one of two ways, either:
 195      * <code> component.setDropTarget(droptarget); </code>
 196      * or <code> droptarget.setComponent(component); </code>
 197      * <P>
 198      * The Component will receive drops only if it is enabled.
 199      * @param c The new <code>Component</code> this <code>DropTarget</code>
 200      * is to be associated with.<P>
 201      */
 202 
 203     public synchronized void setComponent(Component c) {
 204         if (component == c || component != null && component.equals(c))
 205             return;
 206 
 207         Component     old;
 208         ComponentPeer oldPeer = null;
 209 
 210         if ((old = component) != null) {
 211             clearAutoscroll();
 212 
 213             component = null;
 214 
 215             if (componentPeer != null) {
 216                 oldPeer = componentPeer;
 217                 removeNotify(componentPeer);
 218             }
 219 
 220             old.setDropTarget(null);
 221 
 222         }
 223 
 224         if ((component = c) != null) try {
 225             c.setDropTarget(this);
 226         } catch (Exception e) { // undo the change
 227             if (old != null) {
 228                 old.setDropTarget(this);
 229                 addNotify(oldPeer);
 230             }
 231         }
 232     }
 233 
 234     /**
 235      * Gets the <code>Component</code> associated
 236      * with this <code>DropTarget</code>.
 237      * <P>
 238      * @return the current <code>Component</code>
 239      */
 240 
 241     public synchronized Component getComponent() {
 242         return component;
 243     }
 244 
 245     /**
 246      * Sets the default acceptable actions for this <code>DropTarget</code>
 247      * <P>
 248      * @param ops the default actions
 249      * <P>
 250      * @see java.awt.dnd.DnDConstants
 251      */
 252 
 253     public void setDefaultActions(int ops) {
 254         getDropTargetContext().setTargetActions(ops & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE));
 255     }
 256 
 257     /*
 258      * Called by DropTargetContext.setTargetActions()
 259      * with appropriate synchronization.
 260      */
 261     void doSetDefaultActions(int ops) {
 262         actions = ops;
 263     }
 264 
 265     /**
 266      * Gets an <code>int</code> representing the
 267      * current action(s) supported by this <code>DropTarget</code>.
 268      * <P>
 269      * @return the current default actions
 270      */
 271 
 272     public int getDefaultActions() {
 273         return actions;
 274     }
 275 
 276     /**
 277      * Sets the DropTarget active if <code>true</code>,
 278      * inactive if <code>false</code>.
 279      * <P>
 280      * @param isActive sets the <code>DropTarget</code> (in)active.
 281      */
 282 
 283     public synchronized void setActive(boolean isActive) {
 284         if (isActive != active) {
 285             active = isActive;
 286         }
 287 
 288         if (!active) clearAutoscroll();
 289     }
 290 
 291     /**
 292      * Reports whether or not
 293      * this <code>DropTarget</code>
 294      * is currently active (ready to accept drops).
 295      * <P>
 296      * @return <CODE>true</CODE> if active, <CODE>false</CODE> if not
 297      */
 298 
 299     public boolean isActive() {
 300         return active;
 301     }
 302 
 303     /**
 304      * Adds a new <code>DropTargetListener</code> (UNICAST SOURCE).
 305      * <P>
 306      * @param dtl The new <code>DropTargetListener</code>
 307      * <P>
 308      * @throws <code>TooManyListenersException</code> if a
 309      * <code>DropTargetListener</code> is already added to this
 310      * <code>DropTarget</code>.
 311      */
 312 
 313     public synchronized void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
 314         if (dtl == null) return;
 315 
 316         if (equals(dtl)) throw new IllegalArgumentException("DropTarget may not be its own Listener");
 317 
 318         if (dtListener == null)
 319             dtListener = dtl;
 320         else
 321             throw new TooManyListenersException();
 322     }
 323 
 324     /**
 325      * Removes the current <code>DropTargetListener</code> (UNICAST SOURCE).
 326      * <P>
 327      * @param dtl the DropTargetListener to deregister.
 328      */
 329 
 330     public synchronized void removeDropTargetListener(DropTargetListener dtl) {
 331         if (dtl != null && dtListener != null) {
 332             if(dtListener.equals(dtl))
 333                 dtListener = null;
 334             else
 335                 throw new IllegalArgumentException("listener mismatch");
 336         }
 337     }
 338 
 339     /**
 340      * Calls <code>dragEnter</code> on the registered
 341      * <code>DropTargetListener</code> and passes it
 342      * the specified <code>DropTargetDragEvent</code>.
 343      * Has no effect if this <code>DropTarget</code>
 344      * is not active.
 345      *
 346      * @param dtde the <code>DropTargetDragEvent</code>
 347      *
 348      * @throws NullPointerException if this <code>DropTarget</code>
 349      *         is active and <code>dtde</code> is <code>null</code>
 350      *
 351      * @see #isActive
 352      */
 353     public synchronized void dragEnter(DropTargetDragEvent dtde) {
 354         isDraggingInside = true;
 355 
 356         if (!active) return;
 357 
 358         if (dtListener != null) {
 359             dtListener.dragEnter(dtde);
 360         } else
 361             dtde.getDropTargetContext().setTargetActions(DnDConstants.ACTION_NONE);
 362 
 363         initializeAutoscrolling(dtde.getLocation());
 364     }
 365 
 366     /**
 367      * Calls <code>dragOver</code> on the registered
 368      * <code>DropTargetListener</code> and passes it
 369      * the specified <code>DropTargetDragEvent</code>.
 370      * Has no effect if this <code>DropTarget</code>
 371      * is not active.
 372      *
 373      * @param dtde the <code>DropTargetDragEvent</code>
 374      *
 375      * @throws NullPointerException if this <code>DropTarget</code>
 376      *         is active and <code>dtde</code> is <code>null</code>
 377      *
 378      * @see #isActive
 379      */
 380     public synchronized void dragOver(DropTargetDragEvent dtde) {
 381         if (!active) return;
 382 
 383         if (dtListener != null && active) dtListener.dragOver(dtde);
 384 
 385         updateAutoscroll(dtde.getLocation());
 386     }
 387 
 388     /**
 389      * Calls <code>dropActionChanged</code> on the registered
 390      * <code>DropTargetListener</code> and passes it
 391      * the specified <code>DropTargetDragEvent</code>.
 392      * Has no effect if this <code>DropTarget</code>
 393      * is not active.
 394      *
 395      * @param dtde the <code>DropTargetDragEvent</code>
 396      *
 397      * @throws NullPointerException if this <code>DropTarget</code>
 398      *         is active and <code>dtde</code> is <code>null</code>
 399      *
 400      * @see #isActive
 401      */
 402     public synchronized void dropActionChanged(DropTargetDragEvent dtde) {
 403         if (!active) return;
 404 
 405         if (dtListener != null) dtListener.dropActionChanged(dtde);
 406 
 407         updateAutoscroll(dtde.getLocation());
 408     }
 409 
 410     /**
 411      * Calls <code>dragExit</code> on the registered
 412      * <code>DropTargetListener</code> and passes it
 413      * the specified <code>DropTargetEvent</code>.
 414      * Has no effect if this <code>DropTarget</code>
 415      * is not active.
 416      * <p>
 417      * This method itself does not throw any exception
 418      * for null parameter but for exceptions thrown by
 419      * the respective method of the listener.
 420      *
 421      * @param dte the <code>DropTargetEvent</code>
 422      *
 423      * @see #isActive
 424      */
 425     public synchronized void dragExit(DropTargetEvent dte) {
 426         isDraggingInside = false;
 427 
 428         if (!active) return;
 429 
 430         if (dtListener != null && active) dtListener.dragExit(dte);
 431 
 432         clearAutoscroll();
 433     }
 434 
 435     /**
 436      * Calls <code>drop</code> on the registered
 437      * <code>DropTargetListener</code> and passes it
 438      * the specified <code>DropTargetDropEvent</code>
 439      * if this <code>DropTarget</code> is active.
 440      *
 441      * @param dtde the <code>DropTargetDropEvent</code>
 442      *
 443      * @throws NullPointerException if <code>dtde</code> is null
 444      *         and at least one of the following is true: this
 445      *         <code>DropTarget</code> is not active, or there is
 446      *         no a <code>DropTargetListener</code> registered.
 447      *
 448      * @see #isActive
 449      */
 450     public synchronized void drop(DropTargetDropEvent dtde) {
 451         isDraggingInside = false;
 452 
 453         clearAutoscroll();
 454 
 455         if (dtListener != null && active)
 456             dtListener.drop(dtde);
 457         else { // we should'nt get here ...
 458             dtde.rejectDrop();
 459         }
 460     }
 461 
 462     /**
 463      * Gets the <code>FlavorMap</code>
 464      * associated with this <code>DropTarget</code>.
 465      * If no <code>FlavorMap</code> has been set for this
 466      * <code>DropTarget</code>, it is associated with the default
 467      * <code>FlavorMap</code>.
 468      * <P>
 469      * @return the FlavorMap for this DropTarget
 470      */
 471 
 472     public FlavorMap getFlavorMap() { return flavorMap; }
 473 
 474     /**
 475      * Sets the <code>FlavorMap</code> associated
 476      * with this <code>DropTarget</code>.
 477      * <P>
 478      * @param fm the new <code>FlavorMap</code>, or null to
 479      * associate the default FlavorMap with this DropTarget.
 480      */
 481 
 482     public void setFlavorMap(FlavorMap fm) {
 483         flavorMap = fm == null ? SystemFlavorMap.getDefaultFlavorMap() : fm;
 484     }
 485 
 486     /**
 487      * Notify the DropTarget that it has been associated with a Component
 488      *
 489      **********************************************************************
 490      * This method is usually called from java.awt.Component.addNotify() of
 491      * the Component associated with this DropTarget to notify the DropTarget
 492      * that a ComponentPeer has been associated with that Component.
 493      *
 494      * Calling this method, other than to notify this DropTarget of the
 495      * association of the ComponentPeer with the Component may result in
 496      * a malfunction of the DnD system.
 497      **********************************************************************
 498      * <P>
 499      * @param peer The Peer of the Component we are associated with!
 500      *
 501      */
 502 
 503     public void addNotify(ComponentPeer peer) {
 504         if (peer == componentPeer) return;
 505 
 506         componentPeer = peer;
 507 
 508         for (Component c = component;
 509              c != null && peer instanceof LightweightPeer; c = c.getParent()) {
 510             peer = c.getPeer();
 511         }
 512 
 513         if (peer instanceof DropTargetPeer) {
 514             nativePeer = peer;
 515             ((DropTargetPeer)peer).addDropTarget(this);
 516         } else {
 517             nativePeer = null;
 518         }
 519     }
 520 
 521     /**
 522      * Notify the DropTarget that it has been disassociated from a Component
 523      *
 524      **********************************************************************
 525      * This method is usually called from java.awt.Component.removeNotify() of
 526      * the Component associated with this DropTarget to notify the DropTarget
 527      * that a ComponentPeer has been disassociated with that Component.
 528      *
 529      * Calling this method, other than to notify this DropTarget of the
 530      * disassociation of the ComponentPeer from the Component may result in
 531      * a malfunction of the DnD system.
 532      **********************************************************************
 533      * <P>
 534      * @param peer The Peer of the Component we are being disassociated from!
 535      */
 536 
 537     public void removeNotify(ComponentPeer peer) {
 538         if (nativePeer != null)
 539             ((DropTargetPeer)nativePeer).removeDropTarget(this);
 540 
 541         if (isDraggingInside) {
 542             dragExit(new DropTargetEvent(getDropTargetContext()));
 543         }
 544 
 545         componentPeer = nativePeer = null;
 546     }
 547 
 548     /**
 549      * Gets the <code>DropTargetContext</code> associated
 550      * with this <code>DropTarget</code>.
 551      * <P>
 552      * @return the <code>DropTargetContext</code> associated with this <code>DropTarget</code>.
 553      */
 554 
 555     public DropTargetContext getDropTargetContext() {
 556         return dropTargetContext;
 557     }
 558 
 559     /**
 560      * Creates the DropTargetContext associated with this DropTarget.
 561      * Subclasses may override this method to instantiate their own
 562      * DropTargetContext subclass.
 563      *
 564      * This call is typically *only* called by the platform's
 565      * DropTargetContextPeer as a drag operation encounters this
 566      * DropTarget. Accessing the Context while no Drag is current
 567      * has undefined results.
 568      */
 569 
 570     protected DropTargetContext createDropTargetContext() {
 571         return new DropTargetContext(this);
 572     }
 573 
 574     /**
 575      * Serializes this <code>DropTarget</code>. Performs default serialization,
 576      * and then writes out this object's <code>DropTargetListener</code> if and
 577      * only if it can be serialized. If not, <code>null</code> is written
 578      * instead.
 579      *
 580      * @serialData The default serializable fields, in alphabetical order,
 581      *             followed by either a <code>DropTargetListener</code>
 582      *             instance, or <code>null</code>.
 583      * @since 1.4
 584      */
 585     private void writeObject(ObjectOutputStream s) throws IOException {
 586         s.defaultWriteObject();
 587 
 588         s.writeObject(SerializationTester.test(dtListener)
 589                       ? dtListener : null);
 590     }
 591 
 592     /**
 593      * Deserializes this <code>DropTarget</code>. This method first performs
 594      * default deserialization for all non-<code>transient</code> fields. An
 595      * attempt is then made to deserialize this object's
 596      * <code>DropTargetListener</code> as well. This is first attempted by
 597      * deserializing the field <code>dtListener</code>, because, in releases
 598      * prior to 1.4, a non-<code>transient</code> field of this name stored the
 599      * <code>DropTargetListener</code>. If this fails, the next object in the
 600      * stream is used instead.
 601      *
 602      * @since 1.4
 603      */
 604     private void readObject(ObjectInputStream s)
 605         throws ClassNotFoundException, IOException
 606     {
 607         ObjectInputStream.GetField f = s.readFields();
 608 
 609         try {
 610             dropTargetContext =
 611                 (DropTargetContext)f.get("dropTargetContext", null);
 612         } catch (IllegalArgumentException e) {
 613             // Pre-1.4 support. 'dropTargetContext' was previoulsy transient
 614         }
 615         if (dropTargetContext == null) {
 616             dropTargetContext = createDropTargetContext();
 617         }
 618 
 619         component = (Component)f.get("component", null);
 620         actions = f.get("actions", DnDConstants.ACTION_COPY_OR_MOVE);
 621         active = f.get("active", true);
 622 
 623         // Pre-1.4 support. 'dtListener' was previously non-transient
 624         try {
 625             dtListener = (DropTargetListener)f.get("dtListener", null);
 626         } catch (IllegalArgumentException e) {
 627             // 1.4-compatible byte stream. 'dtListener' was written explicitly
 628             dtListener = (DropTargetListener)s.readObject();
 629         }
 630     }
 631 
 632     /*********************************************************************/
 633 
 634     /**
 635      * this protected nested class implements autoscrolling
 636      */
 637 
 638     protected static class DropTargetAutoScroller implements ActionListener {
 639 
 640         /**
 641          * construct a DropTargetAutoScroller
 642          * <P>
 643          * @param c the <code>Component</code>
 644          * @param p the <code>Point</code>
 645          */
 646 
 647         protected DropTargetAutoScroller(Component c, Point p) {
 648             super();
 649 
 650             component  = c;
 651             autoScroll = (Autoscroll)component;
 652 
 653             Toolkit t  = Toolkit.getDefaultToolkit();
 654 
 655             Integer    initial  = Integer.valueOf(100);
 656             Integer    interval = Integer.valueOf(100);
 657 
 658             try {
 659                 initial = (Integer)t.getDesktopProperty("DnD.Autoscroll.initialDelay");
 660             } catch (Exception e) {
 661                 // ignore
 662             }
 663 
 664             try {
 665                 interval = (Integer)t.getDesktopProperty("DnD.Autoscroll.interval");
 666             } catch (Exception e) {
 667                 // ignore
 668             }
 669 
 670             timer  = new Timer(interval.intValue(), this);
 671 
 672             timer.setCoalesce(true);
 673             timer.setInitialDelay(initial.intValue());
 674 
 675             locn = p;
 676             prev = p;
 677 
 678             try {
 679                 hysteresis = ((Integer)t.getDesktopProperty("DnD.Autoscroll.cursorHysteresis")).intValue();
 680             } catch (Exception e) {
 681                 // ignore
 682             }
 683 
 684             timer.start();
 685         }
 686 
 687         /**
 688          * update the geometry of the autoscroll region
 689          */
 690 
 691         private void updateRegion() {
 692            Insets    i    = autoScroll.getAutoscrollInsets();
 693            Dimension size = component.getSize();
 694 
 695            if (size.width != outer.width || size.height != outer.height)
 696                 outer.reshape(0, 0, size.width, size.height);
 697 
 698            if (inner.x != i.left || inner.y != i.top)
 699                 inner.setLocation(i.left, i.top);
 700 
 701            int newWidth  = size.width -  (i.left + i.right);
 702            int newHeight = size.height - (i.top  + i.bottom);
 703 
 704            if (newWidth != inner.width || newHeight != inner.height)
 705                 inner.setSize(newWidth, newHeight);
 706 
 707         }
 708 
 709         /**
 710          * cause autoscroll to occur
 711          * <P>
 712          * @param newLocn the <code>Point</code>
 713          */
 714 
 715         protected synchronized void updateLocation(Point newLocn) {
 716             prev = locn;
 717             locn = newLocn;
 718 
 719             if (Math.abs(locn.x - prev.x) > hysteresis ||
 720                 Math.abs(locn.y - prev.y) > hysteresis) {
 721                 if (timer.isRunning()) timer.stop();
 722             } else {
 723                 if (!timer.isRunning()) timer.start();
 724             }
 725         }
 726 
 727         /**
 728          * cause autoscrolling to stop
 729          */
 730 
 731         protected void stop() { timer.stop(); }
 732 
 733         /**
 734          * cause autoscroll to occur
 735          * <P>
 736          * @param e the <code>ActionEvent</code>
 737          */
 738 
 739         public synchronized void actionPerformed(ActionEvent e) {
 740             updateRegion();
 741 
 742             if (outer.contains(locn) && !inner.contains(locn))
 743                 autoScroll.autoscroll(locn);
 744         }
 745 
 746         /*
 747          * fields
 748          */
 749 
 750         private Component  component;
 751         private Autoscroll autoScroll;
 752 
 753         private Timer      timer;
 754 
 755         private Point      locn;
 756         private Point      prev;
 757 
 758         private Rectangle  outer = new Rectangle();
 759         private Rectangle  inner = new Rectangle();
 760 
 761         private int        hysteresis = 10;
 762     }
 763 
 764     /*********************************************************************/
 765 
 766     /**
 767      * create an embedded autoscroller
 768      * <P>
 769      * @param c the <code>Component</code>
 770      * @param p the <code>Point</code>
 771      */
 772 
 773     protected DropTargetAutoScroller createDropTargetAutoScroller(Component c, Point p) {
 774         return new DropTargetAutoScroller(c, p);
 775     }
 776 
 777     /**
 778      * initialize autoscrolling
 779      * <P>
 780      * @param p the <code>Point</code>
 781      */
 782 
 783     protected void initializeAutoscrolling(Point p) {
 784         if (component == null || !(component instanceof Autoscroll)) return;
 785 
 786         autoScroller = createDropTargetAutoScroller(component, p);
 787     }
 788 
 789     /**
 790      * update autoscrolling with current cursor locn
 791      * <P>
 792      * @param dragCursorLocn the <code>Point</code>
 793      */
 794 
 795     protected void updateAutoscroll(Point dragCursorLocn) {
 796         if (autoScroller != null) autoScroller.updateLocation(dragCursorLocn);
 797     }
 798 
 799     /**
 800      * clear autoscrolling
 801      */
 802 
 803     protected void clearAutoscroll() {
 804         if (autoScroller != null) {
 805             autoScroller.stop();
 806             autoScroller = null;
 807         }
 808     }
 809 
 810     /**
 811      * The DropTargetContext associated with this DropTarget.
 812      *
 813      * @serial
 814      */
 815     private DropTargetContext dropTargetContext = createDropTargetContext();
 816 
 817     /**
 818      * The Component associated with this DropTarget.
 819      *
 820      * @serial
 821      */
 822     private Component component;
 823 
 824     /*
 825      * That Component's  Peer
 826      */
 827     private transient ComponentPeer componentPeer;
 828 
 829     /*
 830      * That Component's "native" Peer
 831      */
 832     private transient ComponentPeer nativePeer;
 833 
 834 
 835     /**
 836      * Default permissible actions supported by this DropTarget.
 837      *
 838      * @see #setDefaultActions
 839      * @see #getDefaultActions
 840      * @serial
 841      */
 842     int     actions = DnDConstants.ACTION_COPY_OR_MOVE;
 843 
 844     /**
 845      * <code>true</code> if the DropTarget is accepting Drag & Drop operations.
 846      *
 847      * @serial
 848      */
 849     boolean active = true;
 850 
 851     /*
 852      * the auto scrolling object
 853      */
 854 
 855     private transient DropTargetAutoScroller autoScroller;
 856 
 857     /*
 858      * The delegate
 859      */
 860 
 861     private transient DropTargetListener dtListener;
 862 
 863     /*
 864      * The FlavorMap
 865      */
 866 
 867     private transient FlavorMap flavorMap;
 868 
 869     /*
 870      * If the dragging is currently inside this drop target
 871      */
 872     private transient boolean isDraggingInside;
 873 }