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