001 /* DropTarget.java --
002 Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt.dnd;
040
041 import java.awt.Component;
042 import java.awt.GraphicsEnvironment;
043 import java.awt.HeadlessException;
044 import java.awt.Insets;
045 import java.awt.Point;
046 import java.awt.Rectangle;
047 import java.awt.datatransfer.FlavorMap;
048 import java.awt.datatransfer.SystemFlavorMap;
049 import java.awt.dnd.peer.DropTargetPeer;
050 import java.awt.event.ActionEvent;
051 import java.awt.event.ActionListener;
052 import java.awt.peer.ComponentPeer;
053 import java.awt.peer.LightweightPeer;
054 import java.io.Serializable;
055 import java.util.EventListener;
056 import java.util.TooManyListenersException;
057
058 import javax.swing.Timer;
059
060 /**
061 * @author Michael Koch
062 * @since 1.2
063 */
064 public 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