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