001/* DropTarget.java -- 
002   Copyright (C) 2002, 2003, 2004  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt.dnd;
040
041import java.awt.Component;
042import java.awt.GraphicsEnvironment;
043import java.awt.HeadlessException;
044import java.awt.Insets;
045import java.awt.Point;
046import java.awt.Rectangle;
047import java.awt.datatransfer.FlavorMap;
048import java.awt.datatransfer.SystemFlavorMap;
049import java.awt.dnd.peer.DropTargetPeer;
050import java.awt.event.ActionEvent;
051import java.awt.event.ActionListener;
052import java.awt.peer.ComponentPeer;
053import java.awt.peer.LightweightPeer;
054import java.io.Serializable;
055import java.util.EventListener;
056import java.util.TooManyListenersException;
057
058import javax.swing.Timer;
059
060/**
061 * @author Michael Koch
062 * @since 1.2
063 */
064public class DropTarget
065  implements DropTargetListener, EventListener, Serializable
066{
067  /**
068   * Compatible with JDK 1.2+
069   */
070  private static final long serialVersionUID = -6283860791671019047L;
071
072  protected static class DropTargetAutoScroller
073    implements ActionListener
074  {
075    /**
076     * The threshold that keeps the autoscroller running.
077     */
078    private static final int HYSTERESIS = 10;
079
080    /**
081     * The initial timer delay.
082     */
083    private static final int DELAY = 100;
084
085    private Component component;
086    private Point point;
087
088    /**
089     * The timer that triggers autoscrolling.
090     */
091    private Timer timer;
092
093    /**
094     * The outer region of the scroller. This is the component's size.
095     */
096    private Rectangle outer;
097
098    /**
099     * The inner region of the scroller. This is the component size without
100     * the autoscroll insets.
101     */
102    private Rectangle inner;
103
104    protected DropTargetAutoScroller (Component c, Point p)
105    {
106      component = c;
107      point = p;
108      timer = new Timer(DELAY, this);
109      timer.setCoalesce(true);
110      timer.start();
111    }
112
113    protected void updateLocation (Point newLocn)
114    {
115      Point previous = point;
116      point = newLocn;
117      if (Math.abs(point.x - previous.x) > HYSTERESIS
118          || Math.abs(point.y - previous.y) > HYSTERESIS)
119        {
120          if (timer.isRunning())
121            timer.stop();
122        }
123      else
124        {
125          if (! timer.isRunning())
126            timer.start();
127        }
128    }
129
130    protected void stop ()
131    {
132      timer.start();
133    }
134
135    public void actionPerformed (ActionEvent e)
136    {
137      Autoscroll autoScroll = (Autoscroll) component;
138
139      // First synchronize the inner and outer rectangles.
140      Insets i = autoScroll.getAutoscrollInsets();
141      int width = component.getWidth();
142      int height = component.getHeight();
143      if (width != outer.width || height != outer.height)
144        outer.setBounds(0, 0, width, height);
145      if (inner.x != i.left || inner.y != i.top)
146        inner.setLocation(i.left, i.top);
147      int inWidth = width - i.left - i.right;
148      int inHeight = height - i.top - i.bottom;
149      if (inWidth != inner.width || inHeight != inner.height)
150        inner.setSize(inWidth, inHeight);
151
152      // Scroll if the outer rectangle contains the location, but the
153      // inner doesn't.
154      if (outer.contains(point) && ! inner.contains(point))
155        autoScroll.autoscroll(point);
156    }
157  }
158
159  private Component component;
160  private FlavorMap flavorMap;
161  private int actions;
162  private DropTargetPeer peer;
163  private DropTargetContext dropTargetContext;
164  private DropTargetListener dropTargetListener;
165  private DropTarget.DropTargetAutoScroller autoscroller;
166  private boolean active = true;
167    
168  /**
169   * Creates a <code>DropTarget</code> object.
170   *
171   * @exception HeadlessException If GraphicsEnvironment.isHeadless()
172   * returns true.
173   */
174  public DropTarget ()
175  {
176    this (null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
177  }
178  
179  /**
180   * Creates a <code>DropTarget</code> object.
181   *
182   * @exception HeadlessException If GraphicsEnvironment.isHeadless()
183   * returns true.
184   */
185  public DropTarget (Component c, DropTargetListener dtl)
186  {
187    this (c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
188  }
189  
190  /**
191   * Creates a <code>DropTarget</code> object.
192   *
193   * @exception HeadlessException If GraphicsEnvironment.isHeadless()
194   * returns true.
195   */
196  public DropTarget (Component c, int i, DropTargetListener dtl)
197  {
198    this (c, i, dtl, true, null);
199  }
200  
201  /**
202   * Creates a <code>DropTarget</code> object.
203   *
204   * @exception HeadlessException If GraphicsEnvironment.isHeadless()
205   * returns true.
206   */
207  public DropTarget (Component c, int i, DropTargetListener dtl, boolean b)
208  {
209    this (c, i, dtl, b, null);
210  }
211  
212  /**
213   * Creates a <code>DropTarget</code> object.
214   *
215   * @exception HeadlessException If GraphicsEnvironment.isHeadless()
216   * returns true.
217   */
218  public DropTarget (Component c, int i, DropTargetListener dtl, boolean b,
219                     FlavorMap fm)
220  {
221    if (GraphicsEnvironment.isHeadless ())
222      throw new HeadlessException ();
223
224    setComponent(c);
225    setDefaultActions(i);
226    dropTargetListener = dtl;
227    
228    if (fm == null)
229      flavorMap = SystemFlavorMap.getDefaultFlavorMap();
230    else
231      flavorMap = fm;
232    
233    setActive (b);
234    
235    if (c != null)
236      c.setDropTarget(this);
237  }
238
239  /**
240   * Sets the component associated with this drop target object.
241   */
242  public void setComponent (Component c)
243  {
244    if (component != null)
245      clearAutoscroll();
246    component = c;
247  }
248
249  /**
250   * Returns the component associated with this drop target object.
251   */
252  public Component getComponent ()
253  {
254    return component;
255  }
256
257  /**
258   * Sets the default actions.
259   */
260  public void setDefaultActions (int ops)
261  {
262    actions = ops;
263  }
264
265  /**
266   * Returns the default actions.
267   */
268  public int getDefaultActions ()
269  {
270    return actions;
271  }
272
273  public void setActive (boolean active)
274  {
275    this.active = active;
276    if (! active)
277      clearAutoscroll();
278  }
279
280  public boolean isActive()
281  {
282    return active;
283  }
284
285  /**
286   * Adds a new <code>DropTargetListener</code>.
287   * 
288   * @exception TooManyListenersException Sun's JDK does not, despite
289   * documentation, throw this exception here when you install an additional
290   * <code>DropTargetListener</code>.  So to be compatible, we do the same
291   * thing.
292   */
293  public void addDropTargetListener (DropTargetListener dtl)
294    throws TooManyListenersException
295  {
296    if (dtl == null)
297      return;
298    
299    if (dtl.equals(this))
300      throw new IllegalArgumentException();
301    
302    if (dropTargetListener != null)
303      throw new TooManyListenersException();
304    
305    dropTargetListener = dtl;
306  }
307
308  public void removeDropTargetListener(DropTargetListener dtl)
309  {
310    if (dropTargetListener != null)
311      dropTargetListener = null;
312  }
313
314  public void dragEnter(DropTargetDragEvent dtde)
315  {
316    if (active)
317      {
318        if (dropTargetListener != null)
319          dropTargetListener.dragEnter(dtde);
320        initializeAutoscrolling(dtde.getLocation());
321      }
322  }
323
324  public void dragOver(DropTargetDragEvent dtde)
325  {
326    if (active)
327      {
328        if (dropTargetListener != null)
329          dropTargetListener.dragOver(dtde);
330        updateAutoscroll(dtde.getLocation());
331      }
332  }
333
334  public void dropActionChanged(DropTargetDragEvent dtde)
335  {
336    if (active)
337      {
338        if (dropTargetListener != null)
339          dropTargetListener.dropActionChanged(dtde);
340        updateAutoscroll(dtde.getLocation());
341      }
342  }
343
344  public void dragExit(DropTargetEvent dte)
345  {
346    if (active)
347      {
348        if (dropTargetListener != null)
349          dropTargetListener.dragExit(dte);
350        clearAutoscroll();
351      }
352  }
353
354  public void drop(DropTargetDropEvent dtde)
355  {
356    clearAutoscroll();
357    if (dropTargetListener != null)
358      dropTargetListener.drop(dtde);
359  }
360
361  public FlavorMap getFlavorMap()
362  {
363    return flavorMap;
364  }
365
366  public void setFlavorMap(FlavorMap fm)
367  {
368    flavorMap = fm;
369  }
370
371  public void addNotify(ComponentPeer p)
372  {
373    Component c = component;
374    while (c != null && p instanceof LightweightPeer)
375      {
376        p = c.getPeer();
377        c = c.getParent();
378      }
379
380    if (p instanceof DropTargetPeer)
381      {
382        peer = ((DropTargetPeer) p);
383        peer.addDropTarget(this);
384      }
385    else
386      peer = null;
387  }
388
389  public void removeNotify(ComponentPeer p)
390  {
391    ((DropTargetPeer) peer).removeDropTarget(this);
392    peer = null;
393    p = null;
394  }
395
396  public DropTargetContext getDropTargetContext()
397  {
398    if (dropTargetContext == null)
399      dropTargetContext = createDropTargetContext ();
400    
401    return dropTargetContext;
402  }
403
404  protected DropTargetContext createDropTargetContext()
405  {
406    if (dropTargetContext == null)
407      dropTargetContext = new DropTargetContext (this);
408    
409    return dropTargetContext;
410  }
411
412  protected DropTarget.DropTargetAutoScroller createDropTargetAutoScroller
413                                                       (Component c, Point p)
414  {
415    return new DropTarget.DropTargetAutoScroller (c, p);
416  }
417
418  protected void initializeAutoscrolling(Point p)
419  {
420    if (component instanceof Autoscroll) // Checks for null too.
421      autoscroller = createDropTargetAutoScroller (component, p);
422  }
423
424  protected void updateAutoscroll(Point dragCursorLocn)
425  {
426    if (autoscroller != null)
427      autoscroller.updateLocation(dragCursorLocn);
428  }
429
430  protected void clearAutoscroll()
431  {
432    if (autoscroller != null)
433      {
434        autoscroller.stop();
435        autoscroller = null;
436      }
437  }
438} // class DropTarget