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