1 /* 2 * Copyright (c) 1997, 2013, 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.awt.Component; 29 import java.awt.Cursor; 30 import java.awt.GraphicsEnvironment; 31 import java.awt.HeadlessException; 32 import java.awt.Image; 33 import java.awt.Point; 34 import java.awt.Toolkit; 35 import java.awt.datatransfer.FlavorMap; 36 import java.awt.datatransfer.SystemFlavorMap; 37 import java.awt.datatransfer.Transferable; 38 import java.awt.dnd.peer.DragSourceContextPeer; 39 import java.io.IOException; 40 import java.io.ObjectInputStream; 41 import java.io.ObjectOutputStream; 42 import java.io.Serializable; 43 import java.security.AccessController; 44 import java.util.EventListener; 45 import sun.awt.dnd.SunDragSourceContextPeer; 46 import sun.security.action.GetIntegerAction; 47 48 49 /** 50 * The <code>DragSource</code> is the entity responsible 51 * for the initiation of the Drag 52 * and Drop operation, and may be used in a number of scenarios: 53 * <UL> 54 * <LI>1 default instance per JVM for the lifetime of that JVM. 55 * <LI>1 instance per class of potential Drag Initiator object (e.g 56 * TextField). [implementation dependent] 57 * <LI>1 per instance of a particular 58 * <code>Component</code>, or application specific 59 * object associated with a <code>Component</code> 60 * instance in the GUI. [implementation dependent] 61 * <LI>Some other arbitrary association. [implementation dependent] 62 *</UL> 63 * 64 * Once the <code>DragSource</code> is 65 * obtained, a <code>DragGestureRecognizer</code> should 66 * also be obtained to associate the <code>DragSource</code> 67 * with a particular 68 * <code>Component</code>. 69 * <P> 70 * The initial interpretation of the user's gesture, 71 * and the subsequent starting of the drag operation 72 * are the responsibility of the implementing 73 * <code>Component</code>, which is usually 74 * implemented by a <code>DragGestureRecognizer</code>. 75 *<P> 76 * When a drag gesture occurs, the 77 * <code>DragSource</code>'s 78 * startDrag() method shall be 79 * invoked in order to cause processing 80 * of the user's navigational 81 * gestures and delivery of Drag and Drop 82 * protocol notifications. A 83 * <code>DragSource</code> shall only 84 * permit a single Drag and Drop operation to be 85 * current at any one time, and shall 86 * reject any further startDrag() requests 87 * by throwing an <code>IllegalDnDOperationException</code> 88 * until such time as the extant operation is complete. 89 * <P> 90 * The startDrag() method invokes the 91 * createDragSourceContext() method to 92 * instantiate an appropriate 93 * <code>DragSourceContext</code> 94 * and associate the <code>DragSourceContextPeer</code> 95 * with that. 96 * <P> 97 * If the Drag and Drop System is 98 * unable to initiate a drag operation for 99 * some reason, the startDrag() method throws 100 * a <code>java.awt.dnd.InvalidDnDOperationException</code> 101 * to signal such a condition. Typically this 102 * exception is thrown when the underlying platform 103 * system is either not in a state to 104 * initiate a drag, or the parameters specified are invalid. 105 * <P> 106 * Note that during the drag, the 107 * set of operations exposed by the source 108 * at the start of the drag operation may not change 109 * until the operation is complete. 110 * The operation(s) are constant for the 111 * duration of the operation with respect to the 112 * <code>DragSource</code>. 113 * 114 * @since 1.2 115 */ 116 117 public class DragSource implements Serializable { 118 119 private static final long serialVersionUID = 6236096958971414066L; 120 121 /* 122 * load a system default cursor 123 */ 124 load(String name)125 private static Cursor load(String name) { 126 if (GraphicsEnvironment.isHeadless()) { 127 return null; 128 } 129 130 try { 131 return (Cursor)Toolkit.getDefaultToolkit().getDesktopProperty(name); 132 } catch (Exception e) { 133 e.printStackTrace(); 134 135 throw new RuntimeException("failed to load system cursor: " + name + " : " + e.getMessage()); 136 } 137 } 138 139 140 /** 141 * The default <code>Cursor</code> to use with a copy operation indicating 142 * that a drop is currently allowed. <code>null</code> if 143 * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>. 144 * 145 * @see java.awt.GraphicsEnvironment#isHeadless 146 */ 147 public static final Cursor DefaultCopyDrop = 148 load("DnD.Cursor.CopyDrop"); 149 150 /** 151 * The default <code>Cursor</code> to use with a move operation indicating 152 * that a drop is currently allowed. <code>null</code> if 153 * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>. 154 * 155 * @see java.awt.GraphicsEnvironment#isHeadless 156 */ 157 public static final Cursor DefaultMoveDrop = 158 load("DnD.Cursor.MoveDrop"); 159 160 /** 161 * The default <code>Cursor</code> to use with a link operation indicating 162 * that a drop is currently allowed. <code>null</code> if 163 * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>. 164 * 165 * @see java.awt.GraphicsEnvironment#isHeadless 166 */ 167 public static final Cursor DefaultLinkDrop = 168 load("DnD.Cursor.LinkDrop"); 169 170 /** 171 * The default <code>Cursor</code> to use with a copy operation indicating 172 * that a drop is currently not allowed. <code>null</code> if 173 * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>. 174 * 175 * @see java.awt.GraphicsEnvironment#isHeadless 176 */ 177 public static final Cursor DefaultCopyNoDrop = 178 load("DnD.Cursor.CopyNoDrop"); 179 180 /** 181 * The default <code>Cursor</code> to use with a move operation indicating 182 * that a drop is currently not allowed. <code>null</code> if 183 * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>. 184 * 185 * @see java.awt.GraphicsEnvironment#isHeadless 186 */ 187 public static final Cursor DefaultMoveNoDrop = 188 load("DnD.Cursor.MoveNoDrop"); 189 190 /** 191 * The default <code>Cursor</code> to use with a link operation indicating 192 * that a drop is currently not allowed. <code>null</code> if 193 * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>. 194 * 195 * @see java.awt.GraphicsEnvironment#isHeadless 196 */ 197 public static final Cursor DefaultLinkNoDrop = 198 load("DnD.Cursor.LinkNoDrop"); 199 200 private static final DragSource dflt = 201 (GraphicsEnvironment.isHeadless()) ? null : new DragSource(); 202 203 /** 204 * Internal constants for serialization. 205 */ 206 static final String dragSourceListenerK = "dragSourceL"; 207 static final String dragSourceMotionListenerK = "dragSourceMotionL"; 208 209 /** 210 * Gets the <code>DragSource</code> object associated with 211 * the underlying platform. 212 * 213 * @return the platform DragSource 214 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 215 * returns true 216 * @see java.awt.GraphicsEnvironment#isHeadless 217 */ getDefaultDragSource()218 public static DragSource getDefaultDragSource() { 219 if (GraphicsEnvironment.isHeadless()) { 220 throw new HeadlessException(); 221 } else { 222 return dflt; 223 } 224 } 225 226 /** 227 * Reports 228 * whether or not drag 229 * <code>Image</code> support 230 * is available on the underlying platform. 231 * <P> 232 * @return if the Drag Image support is available on this platform 233 */ 234 isDragImageSupported()235 public static boolean isDragImageSupported() { 236 Toolkit t = Toolkit.getDefaultToolkit(); 237 238 Boolean supported; 239 240 try { 241 supported = (Boolean)Toolkit.getDefaultToolkit().getDesktopProperty("DnD.isDragImageSupported"); 242 243 return supported.booleanValue(); 244 } catch (Exception e) { 245 return false; 246 } 247 } 248 249 /** 250 * Creates a new <code>DragSource</code>. 251 * 252 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 253 * returns true 254 * @see java.awt.GraphicsEnvironment#isHeadless 255 */ DragSource()256 public DragSource() throws HeadlessException { 257 if (GraphicsEnvironment.isHeadless()) { 258 throw new HeadlessException(); 259 } 260 } 261 262 /** 263 * Start a drag, given the <code>DragGestureEvent</code> 264 * that initiated the drag, the initial 265 * <code>Cursor</code> to use, 266 * the <code>Image</code> to drag, 267 * the offset of the <code>Image</code> origin 268 * from the hotspot of the <code>Cursor</code> at 269 * the instant of the trigger, 270 * the <code>Transferable</code> subject data 271 * of the drag, the <code>DragSourceListener</code>, 272 * and the <code>FlavorMap</code>. 273 * <P> 274 * @param trigger the <code>DragGestureEvent</code> that initiated the drag 275 * @param dragCursor the initial {@code Cursor} for this drag operation 276 * or {@code null} for the default cursor handling; 277 * see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> 278 * for more details on the cursor handling mechanism during drag and drop 279 * @param dragImage the image to drag or {@code null} 280 * @param imageOffset the offset of the <code>Image</code> origin from the hotspot 281 * of the <code>Cursor</code> at the instant of the trigger 282 * @param transferable the subject data of the drag 283 * @param dsl the <code>DragSourceListener</code> 284 * @param flavorMap the <code>FlavorMap</code> to use, or <code>null</code> 285 * <P> 286 * @throws java.awt.dnd.InvalidDnDOperationException 287 * if the Drag and Drop 288 * system is unable to initiate a drag operation, or if the user 289 * attempts to start a drag while an existing drag operation 290 * is still executing 291 */ 292 startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl, FlavorMap flavorMap)293 public void startDrag(DragGestureEvent trigger, 294 Cursor dragCursor, 295 Image dragImage, 296 Point imageOffset, 297 Transferable transferable, 298 DragSourceListener dsl, 299 FlavorMap flavorMap) throws InvalidDnDOperationException { 300 301 SunDragSourceContextPeer.setDragDropInProgress(true); 302 303 try { 304 if (flavorMap != null) this.flavorMap = flavorMap; 305 306 DragSourceContextPeer dscp = Toolkit.getDefaultToolkit().createDragSourceContextPeer(trigger); 307 308 DragSourceContext dsc = createDragSourceContext(dscp, 309 trigger, 310 dragCursor, 311 dragImage, 312 imageOffset, 313 transferable, 314 dsl 315 ); 316 317 if (dsc == null) { 318 throw new InvalidDnDOperationException(); 319 } 320 321 dscp.startDrag(dsc, dsc.getCursor(), dragImage, imageOffset); // may throw 322 } catch (RuntimeException e) { 323 SunDragSourceContextPeer.setDragDropInProgress(false); 324 throw e; 325 } 326 } 327 328 /** 329 * Start a drag, given the <code>DragGestureEvent</code> 330 * that initiated the drag, the initial 331 * <code>Cursor</code> to use, 332 * the <code>Transferable</code> subject data 333 * of the drag, the <code>DragSourceListener</code>, 334 * and the <code>FlavorMap</code>. 335 * <P> 336 * @param trigger the <code>DragGestureEvent</code> that 337 * initiated the drag 338 * @param dragCursor the initial {@code Cursor} for this drag operation 339 * or {@code null} for the default cursor handling; 340 * see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> 341 * for more details on the cursor handling mechanism during drag and drop 342 * @param transferable the subject data of the drag 343 * @param dsl the <code>DragSourceListener</code> 344 * @param flavorMap the <code>FlavorMap</code> to use or <code>null</code> 345 * <P> 346 * @throws java.awt.dnd.InvalidDnDOperationException 347 * if the Drag and Drop 348 * system is unable to initiate a drag operation, or if the user 349 * attempts to start a drag while an existing drag operation 350 * is still executing 351 */ 352 startDrag(DragGestureEvent trigger, Cursor dragCursor, Transferable transferable, DragSourceListener dsl, FlavorMap flavorMap)353 public void startDrag(DragGestureEvent trigger, 354 Cursor dragCursor, 355 Transferable transferable, 356 DragSourceListener dsl, 357 FlavorMap flavorMap) throws InvalidDnDOperationException { 358 startDrag(trigger, dragCursor, null, null, transferable, dsl, flavorMap); 359 } 360 361 /** 362 * Start a drag, given the <code>DragGestureEvent</code> 363 * that initiated the drag, the initial <code>Cursor</code> 364 * to use, 365 * the <code>Image</code> to drag, 366 * the offset of the <code>Image</code> origin 367 * from the hotspot of the <code>Cursor</code> 368 * at the instant of the trigger, 369 * the subject data of the drag, and 370 * the <code>DragSourceListener</code>. 371 * <P> 372 * @param trigger the <code>DragGestureEvent</code> that initiated the drag 373 * @param dragCursor the initial {@code Cursor} for this drag operation 374 * or {@code null} for the default cursor handling; 375 * see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> 376 * for more details on the cursor handling mechanism during drag and drop 377 * @param dragImage the <code>Image</code> to drag or <code>null</code> 378 * @param dragOffset the offset of the <code>Image</code> origin from the hotspot 379 * of the <code>Cursor</code> at the instant of the trigger 380 * @param transferable the subject data of the drag 381 * @param dsl the <code>DragSourceListener</code> 382 * <P> 383 * @throws java.awt.dnd.InvalidDnDOperationException 384 * if the Drag and Drop 385 * system is unable to initiate a drag operation, or if the user 386 * attempts to start a drag while an existing drag operation 387 * is still executing 388 */ 389 startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point dragOffset, Transferable transferable, DragSourceListener dsl)390 public void startDrag(DragGestureEvent trigger, 391 Cursor dragCursor, 392 Image dragImage, 393 Point dragOffset, 394 Transferable transferable, 395 DragSourceListener dsl) throws InvalidDnDOperationException { 396 startDrag(trigger, dragCursor, dragImage, dragOffset, transferable, dsl, null); 397 } 398 399 /** 400 * Start a drag, given the <code>DragGestureEvent</code> 401 * that initiated the drag, the initial 402 * <code>Cursor</code> to 403 * use, 404 * the <code>Transferable</code> subject data 405 * of the drag, and the <code>DragSourceListener</code>. 406 * <P> 407 * @param trigger the <code>DragGestureEvent</code> that initiated the drag 408 * @param dragCursor the initial {@code Cursor} for this drag operation 409 * or {@code null} for the default cursor handling; 410 * see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> class 411 * for more details on the cursor handling mechanism during drag and drop 412 * @param transferable the subject data of the drag 413 * @param dsl the <code>DragSourceListener</code> 414 * <P> 415 * @throws java.awt.dnd.InvalidDnDOperationException 416 * if the Drag and Drop 417 * system is unable to initiate a drag operation, or if the user 418 * attempts to start a drag while an existing drag operation 419 * is still executing 420 */ 421 startDrag(DragGestureEvent trigger, Cursor dragCursor, Transferable transferable, DragSourceListener dsl)422 public void startDrag(DragGestureEvent trigger, 423 Cursor dragCursor, 424 Transferable transferable, 425 DragSourceListener dsl) throws InvalidDnDOperationException { 426 startDrag(trigger, dragCursor, null, null, transferable, dsl, null); 427 } 428 429 /** 430 * Creates the {@code DragSourceContext} to handle the current drag 431 * operation. 432 * <p> 433 * To incorporate a new <code>DragSourceContext</code> 434 * subclass, subclass <code>DragSource</code> and 435 * override this method. 436 * <p> 437 * If <code>dragImage</code> is <code>null</code>, no image is used 438 * to represent the drag over feedback for this drag operation, but 439 * <code>NullPointerException</code> is not thrown. 440 * <p> 441 * If <code>dsl</code> is <code>null</code>, no drag source listener 442 * is registered with the created <code>DragSourceContext</code>, 443 * but <code>NullPointerException</code> is not thrown. 444 * 445 * @param dscp The <code>DragSourceContextPeer</code> for this drag 446 * @param dgl The <code>DragGestureEvent</code> that triggered the 447 * drag 448 * @param dragCursor The initial {@code Cursor} for this drag operation 449 * or {@code null} for the default cursor handling; 450 * see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> class 451 * for more details on the cursor handling mechanism during drag and drop 452 * @param dragImage The <code>Image</code> to drag or <code>null</code> 453 * @param imageOffset The offset of the <code>Image</code> origin from the 454 * hotspot of the cursor at the instant of the trigger 455 * @param t The subject data of the drag 456 * @param dsl The <code>DragSourceListener</code> 457 * 458 * @return the <code>DragSourceContext</code> 459 * 460 * @throws NullPointerException if <code>dscp</code> is <code>null</code> 461 * @throws NullPointerException if <code>dgl</code> is <code>null</code> 462 * @throws NullPointerException if <code>dragImage</code> is not 463 * <code>null</code> and <code>imageOffset</code> is <code>null</code> 464 * @throws NullPointerException if <code>t</code> is <code>null</code> 465 * @throws IllegalArgumentException if the <code>Component</code> 466 * associated with the trigger event is <code>null</code>. 467 * @throws IllegalArgumentException if the <code>DragSource</code> for the 468 * trigger event is <code>null</code>. 469 * @throws IllegalArgumentException if the drag action for the 470 * trigger event is <code>DnDConstants.ACTION_NONE</code>. 471 * @throws IllegalArgumentException if the source actions for the 472 * <code>DragGestureRecognizer</code> associated with the trigger 473 * event are equal to <code>DnDConstants.ACTION_NONE</code>. 474 */ 475 createDragSourceContext(DragSourceContextPeer dscp, DragGestureEvent dgl, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable t, DragSourceListener dsl)476 protected DragSourceContext createDragSourceContext(DragSourceContextPeer dscp, DragGestureEvent dgl, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable t, DragSourceListener dsl) { 477 return new DragSourceContext(dscp, dgl, dragCursor, dragImage, imageOffset, t, dsl); 478 } 479 480 /** 481 * This method returns the 482 * <code>FlavorMap</code> for this <code>DragSource</code>. 483 * <P> 484 * @return the <code>FlavorMap</code> for this <code>DragSource</code> 485 */ 486 getFlavorMap()487 public FlavorMap getFlavorMap() { return flavorMap; } 488 489 /** 490 * Creates a new <code>DragGestureRecognizer</code> 491 * that implements the specified 492 * abstract subclass of 493 * <code>DragGestureRecognizer</code>, and 494 * sets the specified <code>Component</code> 495 * and <code>DragGestureListener</code> on 496 * the newly created object. 497 * <P> 498 * @param recognizerAbstractClass the requested abstract type 499 * @param actions the permitted source drag actions 500 * @param c the <code>Component</code> target 501 * @param dgl the <code>DragGestureListener</code> to notify 502 * <P> 503 * @return the new <code>DragGestureRecognizer</code> or <code>null</code> 504 * if the <code>Toolkit.createDragGestureRecognizer</code> method 505 * has no implementation available for 506 * the requested <code>DragGestureRecognizer</code> 507 * subclass and returns <code>null</code> 508 */ 509 510 public <T extends DragGestureRecognizer> T createDragGestureRecognizer(Class<T> recognizerAbstractClass, Component c, int actions, DragGestureListener dgl)511 createDragGestureRecognizer(Class<T> recognizerAbstractClass, 512 Component c, int actions, 513 DragGestureListener dgl) 514 { 515 return Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizerAbstractClass, this, c, actions, dgl); 516 } 517 518 519 /** 520 * Creates a new <code>DragGestureRecognizer</code> 521 * that implements the default 522 * abstract subclass of <code>DragGestureRecognizer</code> 523 * for this <code>DragSource</code>, 524 * and sets the specified <code>Component</code> 525 * and <code>DragGestureListener</code> on the 526 * newly created object. 527 * 528 * For this <code>DragSource</code> 529 * the default is <code>MouseDragGestureRecognizer</code>. 530 * <P> 531 * @param c the <code>Component</code> target for the recognizer 532 * @param actions the permitted source actions 533 * @param dgl the <code>DragGestureListener</code> to notify 534 * <P> 535 * @return the new <code>DragGestureRecognizer</code> or <code>null</code> 536 * if the <code>Toolkit.createDragGestureRecognizer</code> method 537 * has no implementation available for 538 * the requested <code>DragGestureRecognizer</code> 539 * subclass and returns <code>null</code> 540 */ 541 createDefaultDragGestureRecognizer(Component c, int actions, DragGestureListener dgl)542 public DragGestureRecognizer createDefaultDragGestureRecognizer(Component c, int actions, DragGestureListener dgl) { 543 return Toolkit.getDefaultToolkit().createDragGestureRecognizer(MouseDragGestureRecognizer.class, this, c, actions, dgl); 544 } 545 546 /** 547 * Adds the specified <code>DragSourceListener</code> to this 548 * <code>DragSource</code> to receive drag source events during drag 549 * operations intiated with this <code>DragSource</code>. 550 * If a <code>null</code> listener is specified, no action is taken and no 551 * exception is thrown. 552 * 553 * @param dsl the <code>DragSourceListener</code> to add 554 * 555 * @see #removeDragSourceListener 556 * @see #getDragSourceListeners 557 * @since 1.4 558 */ addDragSourceListener(DragSourceListener dsl)559 public void addDragSourceListener(DragSourceListener dsl) { 560 if (dsl != null) { 561 synchronized (this) { 562 listener = DnDEventMulticaster.add(listener, dsl); 563 } 564 } 565 } 566 567 /** 568 * Removes the specified <code>DragSourceListener</code> from this 569 * <code>DragSource</code>. 570 * If a <code>null</code> listener is specified, no action is taken and no 571 * exception is thrown. 572 * If the listener specified by the argument was not previously added to 573 * this <code>DragSource</code>, no action is taken and no exception 574 * is thrown. 575 * 576 * @param dsl the <code>DragSourceListener</code> to remove 577 * 578 * @see #addDragSourceListener 579 * @see #getDragSourceListeners 580 * @since 1.4 581 */ removeDragSourceListener(DragSourceListener dsl)582 public void removeDragSourceListener(DragSourceListener dsl) { 583 if (dsl != null) { 584 synchronized (this) { 585 listener = DnDEventMulticaster.remove(listener, dsl); 586 } 587 } 588 } 589 590 /** 591 * Gets all the <code>DragSourceListener</code>s 592 * registered with this <code>DragSource</code>. 593 * 594 * @return all of this <code>DragSource</code>'s 595 * <code>DragSourceListener</code>s or an empty array if no 596 * such listeners are currently registered 597 * 598 * @see #addDragSourceListener 599 * @see #removeDragSourceListener 600 * @since 1.4 601 */ getDragSourceListeners()602 public DragSourceListener[] getDragSourceListeners() { 603 return getListeners(DragSourceListener.class); 604 } 605 606 /** 607 * Adds the specified <code>DragSourceMotionListener</code> to this 608 * <code>DragSource</code> to receive drag motion events during drag 609 * operations intiated with this <code>DragSource</code>. 610 * If a <code>null</code> listener is specified, no action is taken and no 611 * exception is thrown. 612 * 613 * @param dsml the <code>DragSourceMotionListener</code> to add 614 * 615 * @see #removeDragSourceMotionListener 616 * @see #getDragSourceMotionListeners 617 * @since 1.4 618 */ addDragSourceMotionListener(DragSourceMotionListener dsml)619 public void addDragSourceMotionListener(DragSourceMotionListener dsml) { 620 if (dsml != null) { 621 synchronized (this) { 622 motionListener = DnDEventMulticaster.add(motionListener, dsml); 623 } 624 } 625 } 626 627 /** 628 * Removes the specified <code>DragSourceMotionListener</code> from this 629 * <code>DragSource</code>. 630 * If a <code>null</code> listener is specified, no action is taken and no 631 * exception is thrown. 632 * If the listener specified by the argument was not previously added to 633 * this <code>DragSource</code>, no action is taken and no exception 634 * is thrown. 635 * 636 * @param dsml the <code>DragSourceMotionListener</code> to remove 637 * 638 * @see #addDragSourceMotionListener 639 * @see #getDragSourceMotionListeners 640 * @since 1.4 641 */ removeDragSourceMotionListener(DragSourceMotionListener dsml)642 public void removeDragSourceMotionListener(DragSourceMotionListener dsml) { 643 if (dsml != null) { 644 synchronized (this) { 645 motionListener = DnDEventMulticaster.remove(motionListener, dsml); 646 } 647 } 648 } 649 650 /** 651 * Gets all of the <code>DragSourceMotionListener</code>s 652 * registered with this <code>DragSource</code>. 653 * 654 * @return all of this <code>DragSource</code>'s 655 * <code>DragSourceMotionListener</code>s or an empty array if no 656 * such listeners are currently registered 657 * 658 * @see #addDragSourceMotionListener 659 * @see #removeDragSourceMotionListener 660 * @since 1.4 661 */ getDragSourceMotionListeners()662 public DragSourceMotionListener[] getDragSourceMotionListeners() { 663 return getListeners(DragSourceMotionListener.class); 664 } 665 666 /** 667 * Gets all the objects currently registered as 668 * <code><em>Foo</em>Listener</code>s upon this <code>DragSource</code>. 669 * <code><em>Foo</em>Listener</code>s are registered using the 670 * <code>add<em>Foo</em>Listener</code> method. 671 * 672 * @param listenerType the type of listeners requested; this parameter 673 * should specify an interface that descends from 674 * <code>java.util.EventListener</code> 675 * @return an array of all objects registered as 676 * <code><em>Foo</em>Listener</code>s on this 677 * <code>DragSource</code>, or an empty array if no such listeners 678 * have been added 679 * @exception ClassCastException if <code>listenerType</code> 680 * doesn't specify a class or interface that implements 681 * <code>java.util.EventListener</code> 682 * 683 * @see #getDragSourceListeners 684 * @see #getDragSourceMotionListeners 685 * @since 1.4 686 */ getListeners(Class<T> listenerType)687 public <T extends EventListener> T[] getListeners(Class<T> listenerType) { 688 EventListener l = null; 689 if (listenerType == DragSourceListener.class) { 690 l = listener; 691 } else if (listenerType == DragSourceMotionListener.class) { 692 l = motionListener; 693 } 694 return DnDEventMulticaster.getListeners(l, listenerType); 695 } 696 697 /** 698 * This method calls <code>dragEnter</code> on the 699 * <code>DragSourceListener</code>s registered with this 700 * <code>DragSource</code>, and passes them the specified 701 * <code>DragSourceDragEvent</code>. 702 * 703 * @param dsde the <code>DragSourceDragEvent</code> 704 */ processDragEnter(DragSourceDragEvent dsde)705 void processDragEnter(DragSourceDragEvent dsde) { 706 DragSourceListener dsl = listener; 707 if (dsl != null) { 708 dsl.dragEnter(dsde); 709 } 710 } 711 712 /** 713 * This method calls <code>dragOver</code> on the 714 * <code>DragSourceListener</code>s registered with this 715 * <code>DragSource</code>, and passes them the specified 716 * <code>DragSourceDragEvent</code>. 717 * 718 * @param dsde the <code>DragSourceDragEvent</code> 719 */ processDragOver(DragSourceDragEvent dsde)720 void processDragOver(DragSourceDragEvent dsde) { 721 DragSourceListener dsl = listener; 722 if (dsl != null) { 723 dsl.dragOver(dsde); 724 } 725 } 726 727 /** 728 * This method calls <code>dropActionChanged</code> on the 729 * <code>DragSourceListener</code>s registered with this 730 * <code>DragSource</code>, and passes them the specified 731 * <code>DragSourceDragEvent</code>. 732 * 733 * @param dsde the <code>DragSourceDragEvent</code> 734 */ processDropActionChanged(DragSourceDragEvent dsde)735 void processDropActionChanged(DragSourceDragEvent dsde) { 736 DragSourceListener dsl = listener; 737 if (dsl != null) { 738 dsl.dropActionChanged(dsde); 739 } 740 } 741 742 /** 743 * This method calls <code>dragExit</code> on the 744 * <code>DragSourceListener</code>s registered with this 745 * <code>DragSource</code>, and passes them the specified 746 * <code>DragSourceEvent</code>. 747 * 748 * @param dse the <code>DragSourceEvent</code> 749 */ processDragExit(DragSourceEvent dse)750 void processDragExit(DragSourceEvent dse) { 751 DragSourceListener dsl = listener; 752 if (dsl != null) { 753 dsl.dragExit(dse); 754 } 755 } 756 757 /** 758 * This method calls <code>dragDropEnd</code> on the 759 * <code>DragSourceListener</code>s registered with this 760 * <code>DragSource</code>, and passes them the specified 761 * <code>DragSourceDropEvent</code>. 762 * 763 * @param dsde the <code>DragSourceEvent</code> 764 */ processDragDropEnd(DragSourceDropEvent dsde)765 void processDragDropEnd(DragSourceDropEvent dsde) { 766 DragSourceListener dsl = listener; 767 if (dsl != null) { 768 dsl.dragDropEnd(dsde); 769 } 770 } 771 772 /** 773 * This method calls <code>dragMouseMoved</code> on the 774 * <code>DragSourceMotionListener</code>s registered with this 775 * <code>DragSource</code>, and passes them the specified 776 * <code>DragSourceDragEvent</code>. 777 * 778 * @param dsde the <code>DragSourceEvent</code> 779 */ processDragMouseMoved(DragSourceDragEvent dsde)780 void processDragMouseMoved(DragSourceDragEvent dsde) { 781 DragSourceMotionListener dsml = motionListener; 782 if (dsml != null) { 783 dsml.dragMouseMoved(dsde); 784 } 785 } 786 787 /** 788 * Serializes this <code>DragSource</code>. This method first performs 789 * default serialization. Next, it writes out this object's 790 * <code>FlavorMap</code> if and only if it can be serialized. If not, 791 * <code>null</code> is written instead. Next, it writes out 792 * <code>Serializable</code> listeners registered with this 793 * object. Listeners are written in a <code>null</code>-terminated sequence 794 * of 0 or more pairs. The pair consists of a <code>String</code> and an 795 * <code>Object</code>; the <code>String</code> indicates the type of the 796 * <code>Object</code> and is one of the following: 797 * <ul> 798 * <li><code>dragSourceListenerK</code> indicating a 799 * <code>DragSourceListener</code> object; 800 * <li><code>dragSourceMotionListenerK</code> indicating a 801 * <code>DragSourceMotionListener</code> object. 802 * </ul> 803 * 804 * @serialData Either a <code>FlavorMap</code> instance, or 805 * <code>null</code>, followed by a <code>null</code>-terminated 806 * sequence of 0 or more pairs; the pair consists of a 807 * <code>String</code> and an <code>Object</code>; the 808 * <code>String</code> indicates the type of the <code>Object</code> 809 * and is one of the following: 810 * <ul> 811 * <li><code>dragSourceListenerK</code> indicating a 812 * <code>DragSourceListener</code> object; 813 * <li><code>dragSourceMotionListenerK</code> indicating a 814 * <code>DragSourceMotionListener</code> object. 815 * </ul>. 816 * @since 1.4 817 */ writeObject(ObjectOutputStream s)818 private void writeObject(ObjectOutputStream s) throws IOException { 819 s.defaultWriteObject(); 820 821 s.writeObject(SerializationTester.test(flavorMap) ? flavorMap : null); 822 823 DnDEventMulticaster.save(s, dragSourceListenerK, listener); 824 DnDEventMulticaster.save(s, dragSourceMotionListenerK, motionListener); 825 s.writeObject(null); 826 } 827 828 /** 829 * Deserializes this <code>DragSource</code>. This method first performs 830 * default deserialization. Next, this object's <code>FlavorMap</code> is 831 * deserialized by using the next object in the stream. 832 * If the resulting <code>FlavorMap</code> is <code>null</code>, this 833 * object's <code>FlavorMap</code> is set to the default FlavorMap for 834 * this thread's <code>ClassLoader</code>. 835 * Next, this object's listeners are deserialized by reading a 836 * <code>null</code>-terminated sequence of 0 or more key/value pairs 837 * from the stream: 838 * <ul> 839 * <li>If a key object is a <code>String</code> equal to 840 * <code>dragSourceListenerK</code>, a <code>DragSourceListener</code> is 841 * deserialized using the corresponding value object and added to this 842 * <code>DragSource</code>. 843 * <li>If a key object is a <code>String</code> equal to 844 * <code>dragSourceMotionListenerK</code>, a 845 * <code>DragSourceMotionListener</code> is deserialized using the 846 * corresponding value object and added to this <code>DragSource</code>. 847 * <li>Otherwise, the key/value pair is skipped. 848 * </ul> 849 * 850 * @see java.awt.datatransfer.SystemFlavorMap#getDefaultFlavorMap 851 * @since 1.4 852 */ readObject(ObjectInputStream s)853 private void readObject(ObjectInputStream s) 854 throws ClassNotFoundException, IOException { 855 s.defaultReadObject(); 856 857 // 'flavorMap' was written explicitly 858 flavorMap = (FlavorMap)s.readObject(); 859 860 // Implementation assumes 'flavorMap' is never null. 861 if (flavorMap == null) { 862 flavorMap = SystemFlavorMap.getDefaultFlavorMap(); 863 } 864 865 Object keyOrNull; 866 while (null != (keyOrNull = s.readObject())) { 867 String key = ((String)keyOrNull).intern(); 868 869 if (dragSourceListenerK == key) { 870 addDragSourceListener((DragSourceListener)(s.readObject())); 871 } else if (dragSourceMotionListenerK == key) { 872 addDragSourceMotionListener( 873 (DragSourceMotionListener)(s.readObject())); 874 } else { 875 // skip value for unrecognized key 876 s.readObject(); 877 } 878 } 879 } 880 881 /** 882 * Returns the drag gesture motion threshold. The drag gesture motion threshold 883 * defines the recommended behavior for {@link MouseDragGestureRecognizer}s. 884 * <p> 885 * If the system property <code>awt.dnd.drag.threshold</code> is set to 886 * a positive integer, this method returns the value of the system property; 887 * otherwise if a pertinent desktop property is available and supported by 888 * the implementation of the Java platform, this method returns the value of 889 * that property; otherwise this method returns some default value. 890 * The pertinent desktop property can be queried using 891 * <code>java.awt.Toolkit.getDesktopProperty("DnD.gestureMotionThreshold")</code>. 892 * 893 * @return the drag gesture motion threshold 894 * @see MouseDragGestureRecognizer 895 * @since 1.5 896 */ getDragThreshold()897 public static int getDragThreshold() { 898 int ts = AccessController.doPrivileged( 899 new GetIntegerAction("awt.dnd.drag.threshold", 0)).intValue(); 900 if (ts > 0) { 901 return ts; 902 } else { 903 Integer td = (Integer)Toolkit.getDefaultToolkit(). 904 getDesktopProperty("DnD.gestureMotionThreshold"); 905 if (td != null) { 906 return td.intValue(); 907 } 908 } 909 return 5; 910 } 911 912 /* 913 * fields 914 */ 915 916 private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap(); 917 918 private transient DragSourceListener listener; 919 920 private transient DragSourceMotionListener motionListener; 921 } 922