blob: e1f81336ffafbe091342868e6d108b7e3ff1ca4c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt.dnd;
27
28import java.awt.Component;
29import java.awt.Cursor;
30import java.awt.GraphicsEnvironment;
31import java.awt.HeadlessException;
32import java.awt.Image;
33import java.awt.Point;
34import java.awt.Toolkit;
35import java.awt.datatransfer.FlavorMap;
36import java.awt.datatransfer.SystemFlavorMap;
37import java.awt.datatransfer.Transferable;
38import java.awt.dnd.peer.DragSourceContextPeer;
39import java.io.IOException;
40import java.io.ObjectInputStream;
41import java.io.ObjectOutputStream;
42import java.io.Serializable;
43import java.security.AccessController;
44import java.util.EventListener;
45import sun.awt.dnd.SunDragSourceContextPeer;
46import 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
117public class DragSource implements Serializable {
118
119 private static final long serialVersionUID = 6236096958971414066L;
120
121 /*
122 * load a system default cursor
123 */
124
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 */
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
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 */
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
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
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
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
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
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
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
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
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 */
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 */
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 */
602 public DragSourceListener[] getDragSourceListeners() {
603 return (DragSourceListener[])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 */
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 */
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 */
662 public DragSourceMotionListener[] getDragSourceMotionListeners() {
663 return (DragSourceMotionListener[])
664 getListeners(DragSourceMotionListener.class);
665 }
666
667 /**
668 * Gets all the objects currently registered as
669 * <code><em>Foo</em>Listener</code>s upon this <code>DragSource</code>.
670 * <code><em>Foo</em>Listener</code>s are registered using the
671 * <code>add<em>Foo</em>Listener</code> method.
672 *
673 * @param listenerType the type of listeners requested; this parameter
674 * should specify an interface that descends from
675 * <code>java.util.EventListener</code>
676 * @return an array of all objects registered as
677 * <code><em>Foo</em>Listener</code>s on this
678 * <code>DragSource</code>, or an empty array if no such listeners
679 * have been added
680 * @exception <code>ClassCastException</code> if <code>listenerType</code>
681 * doesn't specify a class or interface that implements
682 * <code>java.util.EventListener</code>
683 *
684 * @see #getDragSourceListeners
685 * @see #getDragSourceMotionListeners
686 * @since 1.4
687 */
688 public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
689 EventListener l = null;
690 if (listenerType == DragSourceListener.class) {
691 l = listener;
692 } else if (listenerType == DragSourceMotionListener.class) {
693 l = motionListener;
694 }
695 return DnDEventMulticaster.getListeners(l, listenerType);
696 }
697
698 /**
699 * This method calls <code>dragEnter</code> on the
700 * <code>DragSourceListener</code>s registered with this
701 * <code>DragSource</code>, and passes them the specified
702 * <code>DragSourceDragEvent</code>.
703 *
704 * @param dsde the <code>DragSourceDragEvent</code>
705 */
706 void processDragEnter(DragSourceDragEvent dsde) {
707 DragSourceListener dsl = listener;
708 if (dsl != null) {
709 dsl.dragEnter(dsde);
710 }
711 }
712
713 /**
714 * This method calls <code>dragOver</code> on the
715 * <code>DragSourceListener</code>s registered with this
716 * <code>DragSource</code>, and passes them the specified
717 * <code>DragSourceDragEvent</code>.
718 *
719 * @param dsde the <code>DragSourceDragEvent</code>
720 */
721 void processDragOver(DragSourceDragEvent dsde) {
722 DragSourceListener dsl = listener;
723 if (dsl != null) {
724 dsl.dragOver(dsde);
725 }
726 }
727
728 /**
729 * This method calls <code>dropActionChanged</code> on the
730 * <code>DragSourceListener</code>s registered with this
731 * <code>DragSource</code>, and passes them the specified
732 * <code>DragSourceDragEvent</code>.
733 *
734 * @param dsde the <code>DragSourceDragEvent</code>
735 */
736 void processDropActionChanged(DragSourceDragEvent dsde) {
737 DragSourceListener dsl = listener;
738 if (dsl != null) {
739 dsl.dropActionChanged(dsde);
740 }
741 }
742
743 /**
744 * This method calls <code>dragExit</code> on the
745 * <code>DragSourceListener</code>s registered with this
746 * <code>DragSource</code>, and passes them the specified
747 * <code>DragSourceEvent</code>.
748 *
749 * @param dse the <code>DragSourceEvent</code>
750 */
751 void processDragExit(DragSourceEvent dse) {
752 DragSourceListener dsl = listener;
753 if (dsl != null) {
754 dsl.dragExit(dse);
755 }
756 }
757
758 /**
759 * This method calls <code>dragDropEnd</code> on the
760 * <code>DragSourceListener</code>s registered with this
761 * <code>DragSource</code>, and passes them the specified
762 * <code>DragSourceDropEvent</code>.
763 *
764 * @param dsde the <code>DragSourceEvent</code>
765 */
766 void processDragDropEnd(DragSourceDropEvent dsde) {
767 DragSourceListener dsl = listener;
768 if (dsl != null) {
769 dsl.dragDropEnd(dsde);
770 }
771 }
772
773 /**
774 * This method calls <code>dragMouseMoved</code> on the
775 * <code>DragSourceMotionListener</code>s registered with this
776 * <code>DragSource</code>, and passes them the specified
777 * <code>DragSourceDragEvent</code>.
778 *
779 * @param dsde the <code>DragSourceEvent</code>
780 */
781 void processDragMouseMoved(DragSourceDragEvent dsde) {
782 DragSourceMotionListener dsml = motionListener;
783 if (dsml != null) {
784 dsml.dragMouseMoved(dsde);
785 }
786 }
787
788 /**
789 * Serializes this <code>DragSource</code>. This method first performs
790 * default serialization. Next, it writes out this object's
791 * <code>FlavorMap</code> if and only if it can be serialized. If not,
792 * <code>null</code> is written instead. Next, it writes out
793 * <code>Serializable</code> listeners registered with this
794 * object. Listeners are written in a <code>null</code>-terminated sequence
795 * of 0 or more pairs. The pair consists of a <code>String</code> and an
796 * <code>Object</code>; the <code>String</code> indicates the type of the
797 * <code>Object</code> and is one of the following:
798 * <ul>
799 * <li><code>dragSourceListenerK</code> indicating a
800 * <code>DragSourceListener</code> object;
801 * <li><code>dragSourceMotionListenerK</code> indicating a
802 * <code>DragSourceMotionListener</code> object.
803 * </ul>
804 *
805 * @serialData Either a <code>FlavorMap</code> instance, or
806 * <code>null</code>, followed by a <code>null</code>-terminated
807 * sequence of 0 or more pairs; the pair consists of a
808 * <code>String</code> and an <code>Object</code>; the
809 * <code>String</code> indicates the type of the <code>Object</code>
810 * and is one of the following:
811 * <ul>
812 * <li><code>dragSourceListenerK</code> indicating a
813 * <code>DragSourceListener</code> object;
814 * <li><code>dragSourceMotionListenerK</code> indicating a
815 * <code>DragSourceMotionListener</code> object.
816 * </ul>.
817 * @since 1.4
818 */
819 private void writeObject(ObjectOutputStream s) throws IOException {
820 s.defaultWriteObject();
821
822 s.writeObject(SerializationTester.test(flavorMap) ? flavorMap : null);
823
824 DnDEventMulticaster.save(s, dragSourceListenerK, listener);
825 DnDEventMulticaster.save(s, dragSourceMotionListenerK, motionListener);
826 s.writeObject(null);
827 }
828
829 /**
830 * Deserializes this <code>DragSource</code>. This method first performs
831 * default deserialization. Next, this object's <code>FlavorMap</code> is
832 * deserialized by using the next object in the stream.
833 * If the resulting <code>FlavorMap</code> is <code>null</code>, this
834 * object's <code>FlavorMap</code> is set to the default FlavorMap for
835 * this thread's <code>ClassLoader</code>.
836 * Next, this object's listeners are deserialized by reading a
837 * <code>null</code>-terminated sequence of 0 or more key/value pairs
838 * from the stream:
839 * <ul>
840 * <li>If a key object is a <code>String</code> equal to
841 * <code>dragSourceListenerK</code>, a <code>DragSourceListener</code> is
842 * deserialized using the corresponding value object and added to this
843 * <code>DragSource</code>.
844 * <li>If a key object is a <code>String</code> equal to
845 * <code>dragSourceMotionListenerK</code>, a
846 * <code>DragSourceMotionListener</code> is deserialized using the
847 * corresponding value object and added to this <code>DragSource</code>.
848 * <li>Otherwise, the key/value pair is skipped.
849 * </ul>
850 *
851 * @see java.awt.datatransfer.SystemFlavorMap#getDefaultFlavorMap
852 * @since 1.4
853 */
854 private void readObject(ObjectInputStream s)
855 throws ClassNotFoundException, IOException {
856 s.defaultReadObject();
857
858 // 'flavorMap' was written explicitly
859 flavorMap = (FlavorMap)s.readObject();
860
861 // Implementation assumes 'flavorMap' is never null.
862 if (flavorMap == null) {
863 flavorMap = SystemFlavorMap.getDefaultFlavorMap();
864 }
865
866 Object keyOrNull;
867 while (null != (keyOrNull = s.readObject())) {
868 String key = ((String)keyOrNull).intern();
869
870 if (dragSourceListenerK == key) {
871 addDragSourceListener((DragSourceListener)(s.readObject()));
872 } else if (dragSourceMotionListenerK == key) {
873 addDragSourceMotionListener(
874 (DragSourceMotionListener)(s.readObject()));
875 } else {
876 // skip value for unrecognized key
877 s.readObject();
878 }
879 }
880 }
881
882 /**
883 * Returns the drag gesture motion threshold. The drag gesture motion threshold
884 * defines the recommended behavior for {@link MouseDragGestureRecognizer}s.
885 * <p>
886 * If the system property <code>awt.dnd.drag.threshold</code> is set to
887 * a positive integer, this method returns the value of the system property;
888 * otherwise if a pertinent desktop property is available and supported by
889 * the implementation of the Java platform, this method returns the value of
890 * that property; otherwise this method returns some default value.
891 * The pertinent desktop property can be queried using
892 * <code>java.awt.Toolkit.getDesktopProperty("DnD.gestureMotionThreshold")</code>.
893 *
894 * @return the drag gesture motion threshold
895 * @see MouseDragGestureRecognizer
896 * @since 1.5
897 */
898 public static int getDragThreshold() {
899 int ts = ((Integer)AccessController.doPrivileged(
900 new GetIntegerAction("awt.dnd.drag.threshold", 0))).intValue();
901 if (ts > 0) {
902 return ts;
903 } else {
904 Integer td = (Integer)Toolkit.getDefaultToolkit().
905 getDesktopProperty("DnD.gestureMotionThreshold");
906 if (td != null) {
907 return td.intValue();
908 }
909 }
910 return 5;
911 }
912
913 /*
914 * fields
915 */
916
917 private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap();
918
919 private transient DragSourceListener listener;
920
921 private transient DragSourceMotionListener motionListener;
922}