37 import java.awt.GraphicsEnvironment;
38 import java.awt.HeadlessException;
39 import java.awt.Insets;
40 import java.awt.Point;
41 import java.awt.Rectangle;
42 import java.awt.Toolkit;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.datatransfer.FlavorMap;
46 import java.awt.datatransfer.SystemFlavorMap;
47 import javax.swing.Timer;
48 import java.awt.peer.ComponentPeer;
49 import java.awt.peer.LightweightPeer;
50 import java.awt.dnd.peer.DropTargetPeer;
51
52 import sun.awt.AWTAccessor;
53 import sun.awt.AWTAccessor.ComponentAccessor;
54
55
56 /**
57 * The <code>DropTarget</code> is associated
58 * with a <code>Component</code> when that <code>Component</code>
59 * wishes
60 * to accept drops during Drag and Drop operations.
61 * <P>
62 * Each
63 * <code>DropTarget</code> is associated with a <code>FlavorMap</code>.
64 * The default <code>FlavorMap</code> hereafter designates the
65 * <code>FlavorMap</code> returned by <code>SystemFlavorMap.getDefaultFlavorMap()</code>.
66 *
67 * @since 1.2
68 */
69
70 public class DropTarget implements DropTargetListener, Serializable {
71
72 private static final long serialVersionUID = -6283860791671019047L;
73
74 /**
75 * Creates a new DropTarget given the <code>Component</code>
76 * to associate itself with, an <code>int</code> representing
77 * the default acceptable action(s) to
78 * support, a <code>DropTargetListener</code>
79 * to handle event processing, a <code>boolean</code> indicating
80 * if the <code>DropTarget</code> is currently accepting drops, and
81 * a <code>FlavorMap</code> to use (or null for the default <CODE>FlavorMap</CODE>).
82 * <P>
83 * The Component will receive drops only if it is enabled.
84 * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
85 * @param ops The default acceptable actions for this <code>DropTarget</code>
86 * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
87 * @param act Is the <code>DropTarget</code> accepting drops.
88 * @param fm The <code>FlavorMap</code> to use, or null for the default <CODE>FlavorMap</CODE>
89 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
90 * returns true
91 * @see java.awt.GraphicsEnvironment#isHeadless
92 */
93 public DropTarget(Component c, int ops, DropTargetListener dtl,
94 boolean act, FlavorMap fm)
95 throws HeadlessException
96 {
97 if (GraphicsEnvironment.isHeadless()) {
98 throw new HeadlessException();
99 }
100
101 component = c;
102
103 setDefaultActions(ops);
104
105 if (dtl != null) try {
106 addDropTargetListener(dtl);
107 } catch (TooManyListenersException tmle) {
108 // do nothing!
109 }
110
111 if (c != null) {
112 c.setDropTarget(this);
113 setActive(act);
114 }
115
116 if (fm != null) {
117 flavorMap = fm;
118 } else {
119 flavorMap = SystemFlavorMap.getDefaultFlavorMap();
120 }
121 }
122
123 /**
124 * Creates a <code>DropTarget</code> given the <code>Component</code>
125 * to associate itself with, an <code>int</code> representing
126 * the default acceptable action(s)
127 * to support, a <code>DropTargetListener</code>
128 * to handle event processing, and a <code>boolean</code> indicating
129 * if the <code>DropTarget</code> is currently accepting drops.
130 * <P>
131 * The Component will receive drops only if it is enabled.
132 * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
133 * @param ops The default acceptable actions for this <code>DropTarget</code>
134 * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
135 * @param act Is the <code>DropTarget</code> accepting drops.
136 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
137 * returns true
138 * @see java.awt.GraphicsEnvironment#isHeadless
139 */
140 public DropTarget(Component c, int ops, DropTargetListener dtl,
141 boolean act)
142 throws HeadlessException
143 {
144 this(c, ops, dtl, act, null);
145 }
146
147 /**
148 * Creates a <code>DropTarget</code>.
149 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
150 * returns true
151 * @see java.awt.GraphicsEnvironment#isHeadless
152 */
153 public DropTarget() throws HeadlessException {
154 this(null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
155 }
156
157 /**
158 * Creates a <code>DropTarget</code> given the <code>Component</code>
159 * to associate itself with, and the <code>DropTargetListener</code>
160 * to handle event processing.
161 * <P>
162 * The Component will receive drops only if it is enabled.
163 * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
164 * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
165 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
166 * returns true
167 * @see java.awt.GraphicsEnvironment#isHeadless
168 */
169 public DropTarget(Component c, DropTargetListener dtl)
170 throws HeadlessException
171 {
172 this(c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
173 }
174
175 /**
176 * Creates a <code>DropTarget</code> given the <code>Component</code>
177 * to associate itself with, an <code>int</code> representing
178 * the default acceptable action(s) to support, and a
179 * <code>DropTargetListener</code> to handle event processing.
180 * <P>
181 * The Component will receive drops only if it is enabled.
182 * @param c The <code>Component</code> with which this <code>DropTarget</code> is associated
183 * @param ops The default acceptable actions for this <code>DropTarget</code>
184 * @param dtl The <code>DropTargetListener</code> for this <code>DropTarget</code>
185 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
186 * returns true
187 * @see java.awt.GraphicsEnvironment#isHeadless
188 */
189 public DropTarget(Component c, int ops, DropTargetListener dtl)
190 throws HeadlessException
191 {
192 this(c, ops, dtl, true);
193 }
194
195 /**
196 * Note: this interface is required to permit the safe association
197 * of a DropTarget with a Component in one of two ways, either:
198 * <code> component.setDropTarget(droptarget); </code>
199 * or <code> droptarget.setComponent(component); </code>
200 * <P>
201 * The Component will receive drops only if it is enabled.
202 * @param c The new <code>Component</code> this <code>DropTarget</code>
203 * is to be associated with.
204 */
205
206 public synchronized void setComponent(Component c) {
207 if (component == c || component != null && component.equals(c))
208 return;
209
210 final Component old = component;
211
212 if (old != null) {
213 clearAutoscroll();
214
215 component = null;
216 removeNotify();
217 old.setDropTarget(null);
218
219 }
220
221 if ((component = c) != null) try {
222 c.setDropTarget(this);
223 } catch (Exception e) { // undo the change
224 if (old != null) {
225 old.setDropTarget(this);
226 addNotify();
227 }
228 }
229 }
230
231 /**
232 * Gets the <code>Component</code> associated
233 * with this <code>DropTarget</code>.
234 *
235 * @return the current <code>Component</code>
236 */
237
238 public synchronized Component getComponent() {
239 return component;
240 }
241
242 /**
243 * Sets the default acceptable actions for this <code>DropTarget</code>
244 *
245 * @param ops the default actions
246 * @see java.awt.dnd.DnDConstants
247 */
248
249 public void setDefaultActions(int ops) {
250 getDropTargetContext().setTargetActions(ops & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE));
251 }
252
253 /*
254 * Called by DropTargetContext.setTargetActions()
255 * with appropriate synchronization.
256 */
257 void doSetDefaultActions(int ops) {
258 actions = ops;
259 }
260
261 /**
262 * Gets an <code>int</code> representing the
263 * current action(s) supported by this <code>DropTarget</code>.
264 *
265 * @return the current default actions
266 */
267
268 public int getDefaultActions() {
269 return actions;
270 }
271
272 /**
273 * Sets the DropTarget active if <code>true</code>,
274 * inactive if <code>false</code>.
275 *
276 * @param isActive sets the <code>DropTarget</code> (in)active.
277 */
278
279 public synchronized void setActive(boolean isActive) {
280 if (isActive != active) {
281 active = isActive;
282 }
283
284 if (!active) clearAutoscroll();
285 }
286
287 /**
288 * Reports whether or not
289 * this <code>DropTarget</code>
290 * is currently active (ready to accept drops).
291 *
292 * @return <CODE>true</CODE> if active, <CODE>false</CODE> if not
293 */
294
295 public boolean isActive() {
296 return active;
297 }
298
299 /**
300 * Adds a new <code>DropTargetListener</code> (UNICAST SOURCE).
301 *
302 * @param dtl The new <code>DropTargetListener</code>
303 *
304 * @throws TooManyListenersException if a
305 * <code>DropTargetListener</code> is already added to this
306 * <code>DropTarget</code>.
307 */
308
309 public synchronized void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
310 if (dtl == null) return;
311
312 if (equals(dtl)) throw new IllegalArgumentException("DropTarget may not be its own Listener");
313
314 if (dtListener == null)
315 dtListener = dtl;
316 else
317 throw new TooManyListenersException();
318 }
319
320 /**
321 * Removes the current <code>DropTargetListener</code> (UNICAST SOURCE).
322 *
323 * @param dtl the DropTargetListener to deregister.
324 */
325
326 public synchronized void removeDropTargetListener(DropTargetListener dtl) {
327 if (dtl != null && dtListener != null) {
328 if(dtListener.equals(dtl))
329 dtListener = null;
330 else
331 throw new IllegalArgumentException("listener mismatch");
332 }
333 }
334
335 /**
336 * Calls <code>dragEnter</code> on the registered
337 * <code>DropTargetListener</code> and passes it
338 * the specified <code>DropTargetDragEvent</code>.
339 * Has no effect if this <code>DropTarget</code>
340 * is not active.
341 *
342 * @param dtde the <code>DropTargetDragEvent</code>
343 *
344 * @throws NullPointerException if this <code>DropTarget</code>
345 * is active and <code>dtde</code> is <code>null</code>
346 *
347 * @see #isActive
348 */
349 public synchronized void dragEnter(DropTargetDragEvent dtde) {
350 isDraggingInside = true;
351
352 if (!active) return;
353
354 if (dtListener != null) {
355 dtListener.dragEnter(dtde);
356 } else
357 dtde.getDropTargetContext().setTargetActions(DnDConstants.ACTION_NONE);
358
359 initializeAutoscrolling(dtde.getLocation());
360 }
361
362 /**
363 * Calls <code>dragOver</code> on the registered
364 * <code>DropTargetListener</code> and passes it
365 * the specified <code>DropTargetDragEvent</code>.
366 * Has no effect if this <code>DropTarget</code>
367 * is not active.
368 *
369 * @param dtde the <code>DropTargetDragEvent</code>
370 *
371 * @throws NullPointerException if this <code>DropTarget</code>
372 * is active and <code>dtde</code> is <code>null</code>
373 *
374 * @see #isActive
375 */
376 public synchronized void dragOver(DropTargetDragEvent dtde) {
377 if (!active) return;
378
379 if (dtListener != null && active) dtListener.dragOver(dtde);
380
381 updateAutoscroll(dtde.getLocation());
382 }
383
384 /**
385 * Calls <code>dropActionChanged</code> on the registered
386 * <code>DropTargetListener</code> and passes it
387 * the specified <code>DropTargetDragEvent</code>.
388 * Has no effect if this <code>DropTarget</code>
389 * is not active.
390 *
391 * @param dtde the <code>DropTargetDragEvent</code>
392 *
393 * @throws NullPointerException if this <code>DropTarget</code>
394 * is active and <code>dtde</code> is <code>null</code>
395 *
396 * @see #isActive
397 */
398 public synchronized void dropActionChanged(DropTargetDragEvent dtde) {
399 if (!active) return;
400
401 if (dtListener != null) dtListener.dropActionChanged(dtde);
402
403 updateAutoscroll(dtde.getLocation());
404 }
405
406 /**
407 * Calls <code>dragExit</code> on the registered
408 * <code>DropTargetListener</code> and passes it
409 * the specified <code>DropTargetEvent</code>.
410 * Has no effect if this <code>DropTarget</code>
411 * is not active.
412 * <p>
413 * This method itself does not throw any exception
414 * for null parameter but for exceptions thrown by
415 * the respective method of the listener.
416 *
417 * @param dte the <code>DropTargetEvent</code>
418 *
419 * @see #isActive
420 */
421 public synchronized void dragExit(DropTargetEvent dte) {
422 isDraggingInside = false;
423
424 if (!active) return;
425
426 if (dtListener != null && active) dtListener.dragExit(dte);
427
428 clearAutoscroll();
429 }
430
431 /**
432 * Calls <code>drop</code> on the registered
433 * <code>DropTargetListener</code> and passes it
434 * the specified <code>DropTargetDropEvent</code>
435 * if this <code>DropTarget</code> is active.
436 *
437 * @param dtde the <code>DropTargetDropEvent</code>
438 *
439 * @throws NullPointerException if <code>dtde</code> is null
440 * and at least one of the following is true: this
441 * <code>DropTarget</code> is not active, or there is
442 * no a <code>DropTargetListener</code> registered.
443 *
444 * @see #isActive
445 */
446 public synchronized void drop(DropTargetDropEvent dtde) {
447 isDraggingInside = false;
448
449 clearAutoscroll();
450
451 if (dtListener != null && active)
452 dtListener.drop(dtde);
453 else { // we shouldn't get here ...
454 dtde.rejectDrop();
455 }
456 }
457
458 /**
459 * Gets the <code>FlavorMap</code>
460 * associated with this <code>DropTarget</code>.
461 * If no <code>FlavorMap</code> has been set for this
462 * <code>DropTarget</code>, it is associated with the default
463 * <code>FlavorMap</code>.
464 *
465 * @return the FlavorMap for this DropTarget
466 */
467
468 public FlavorMap getFlavorMap() { return flavorMap; }
469
470 /**
471 * Sets the <code>FlavorMap</code> associated
472 * with this <code>DropTarget</code>.
473 *
474 * @param fm the new <code>FlavorMap</code>, or null to
475 * associate the default FlavorMap with this DropTarget.
476 */
477
478 public void setFlavorMap(FlavorMap fm) {
479 flavorMap = fm == null ? SystemFlavorMap.getDefaultFlavorMap() : fm;
480 }
481
482 /**
483 * Notify the DropTarget that it has been associated with a Component
484 *
485 **********************************************************************
486 * This method is usually called from java.awt.Component.addNotify() of
487 * the Component associated with this DropTarget to notify the DropTarget
488 * that a ComponentPeer has been associated with that Component.
489 *
490 * Calling this method, other than to notify this DropTarget of the
491 * association of the ComponentPeer with the Component may result in
492 * a malfunction of the DnD system.
493 **********************************************************************
494 */
527 * disassociation of the ComponentPeer from the Component may result in
528 * a malfunction of the DnD system.
529 **********************************************************************
530 */
531
532 public void removeNotify() {
533 if (nativePeer != null) {
534 nativePeer.removeDropTarget(this);
535 }
536 componentPeer = null;
537 nativePeer = null;
538
539 synchronized (this) {
540 if (isDraggingInside) {
541 dragExit(new DropTargetEvent(getDropTargetContext()));
542 }
543 }
544 }
545
546 /**
547 * Gets the <code>DropTargetContext</code> associated
548 * with this <code>DropTarget</code>.
549 *
550 * @return the <code>DropTargetContext</code> associated with this <code>DropTarget</code>.
551 */
552
553 public DropTargetContext getDropTargetContext() {
554 return dropTargetContext;
555 }
556
557 /**
558 * Creates the DropTargetContext associated with this DropTarget.
559 * Subclasses may override this method to instantiate their own
560 * DropTargetContext subclass.
561 *
562 * This call is typically *only* called by the platform's
563 * DropTargetContextPeer as a drag operation encounters this
564 * DropTarget. Accessing the Context while no Drag is current
565 * has undefined results.
566 * @return the DropTargetContext associated with this DropTarget
567 */
568
569 protected DropTargetContext createDropTargetContext() {
570 return new DropTargetContext(this);
571 }
572
573 /**
574 * Serializes this <code>DropTarget</code>. Performs default serialization,
575 * and then writes out this object's <code>DropTargetListener</code> if and
576 * only if it can be serialized. If not, <code>null</code> is written
577 * instead.
578 *
579 * @serialData The default serializable fields, in alphabetical order,
580 * followed by either a <code>DropTargetListener</code>
581 * instance, or <code>null</code>.
582 * @since 1.4
583 */
584 private void writeObject(ObjectOutputStream s) throws IOException {
585 s.defaultWriteObject();
586
587 s.writeObject(SerializationTester.test(dtListener)
588 ? dtListener : null);
589 }
590
591 /**
592 * Deserializes this <code>DropTarget</code>. This method first performs
593 * default deserialization for all non-<code>transient</code> fields. An
594 * attempt is then made to deserialize this object's
595 * <code>DropTargetListener</code> as well. This is first attempted by
596 * deserializing the field <code>dtListener</code>, because, in releases
597 * prior to 1.4, a non-<code>transient</code> field of this name stored the
598 * <code>DropTargetListener</code>. If this fails, the next object in the
599 * stream is used instead.
600 *
601 * @since 1.4
602 */
603 private void readObject(ObjectInputStream s)
604 throws ClassNotFoundException, IOException
605 {
606 ObjectInputStream.GetField f = s.readFields();
607
608 try {
609 dropTargetContext =
610 (DropTargetContext)f.get("dropTargetContext", null);
611 } catch (IllegalArgumentException e) {
612 // Pre-1.4 support. 'dropTargetContext' was previously transient
613 }
614 if (dropTargetContext == null) {
615 dropTargetContext = createDropTargetContext();
616 }
617
618 component = (Component)f.get("component", null);
622 // Pre-1.4 support. 'dtListener' was previously non-transient
623 try {
624 dtListener = (DropTargetListener)f.get("dtListener", null);
625 } catch (IllegalArgumentException e) {
626 // 1.4-compatible byte stream. 'dtListener' was written explicitly
627 dtListener = (DropTargetListener)s.readObject();
628 }
629 }
630
631 /*********************************************************************/
632
633 /**
634 * this protected nested class implements autoscrolling
635 */
636
637 protected static class DropTargetAutoScroller implements ActionListener {
638
639 /**
640 * construct a DropTargetAutoScroller
641 *
642 * @param c the <code>Component</code>
643 * @param p the <code>Point</code>
644 */
645
646 protected DropTargetAutoScroller(Component c, Point p) {
647 super();
648
649 component = c;
650 autoScroll = (Autoscroll)component;
651
652 Toolkit t = Toolkit.getDefaultToolkit();
653
654 Integer initial = Integer.valueOf(100);
655 Integer interval = Integer.valueOf(100);
656
657 try {
658 initial = (Integer)t.getDesktopProperty("DnD.Autoscroll.initialDelay");
659 } catch (Exception e) {
660 // ignore
661 }
662
663 try {
692 Insets i = autoScroll.getAutoscrollInsets();
693 Dimension size = component.getSize();
694
695 if (size.width != outer.width || size.height != outer.height)
696 outer.reshape(0, 0, size.width, size.height);
697
698 if (inner.x != i.left || inner.y != i.top)
699 inner.setLocation(i.left, i.top);
700
701 int newWidth = size.width - (i.left + i.right);
702 int newHeight = size.height - (i.top + i.bottom);
703
704 if (newWidth != inner.width || newHeight != inner.height)
705 inner.setSize(newWidth, newHeight);
706
707 }
708
709 /**
710 * cause autoscroll to occur
711 *
712 * @param newLocn the <code>Point</code>
713 */
714
715 protected synchronized void updateLocation(Point newLocn) {
716 prev = locn;
717 locn = newLocn;
718
719 if (Math.abs(locn.x - prev.x) > hysteresis ||
720 Math.abs(locn.y - prev.y) > hysteresis) {
721 if (timer.isRunning()) timer.stop();
722 } else {
723 if (!timer.isRunning()) timer.start();
724 }
725 }
726
727 /**
728 * cause autoscrolling to stop
729 */
730
731 protected void stop() { timer.stop(); }
732
733 /**
734 * cause autoscroll to occur
735 *
736 * @param e the <code>ActionEvent</code>
737 */
738
739 public synchronized void actionPerformed(ActionEvent e) {
740 updateRegion();
741
742 if (outer.contains(locn) && !inner.contains(locn))
743 autoScroll.autoscroll(locn);
744 }
745
746 /*
747 * fields
748 */
749
750 private Component component;
751 private Autoscroll autoScroll;
752
753 private Timer timer;
754
755 private Point locn;
756 private Point prev;
757
758 private Rectangle outer = new Rectangle();
759 private Rectangle inner = new Rectangle();
760
761 private int hysteresis = 10;
762 }
763
764 /*********************************************************************/
765
766 /**
767 * create an embedded autoscroller
768 *
769 * @param c the <code>Component</code>
770 * @param p the <code>Point</code>
771 * @return an embedded autoscroller
772 */
773
774 protected DropTargetAutoScroller createDropTargetAutoScroller(Component c, Point p) {
775 return new DropTargetAutoScroller(c, p);
776 }
777
778 /**
779 * initialize autoscrolling
780 *
781 * @param p the <code>Point</code>
782 */
783
784 protected void initializeAutoscrolling(Point p) {
785 if (component == null || !(component instanceof Autoscroll)) return;
786
787 autoScroller = createDropTargetAutoScroller(component, p);
788 }
789
790 /**
791 * update autoscrolling with current cursor location
792 *
793 * @param dragCursorLocn the <code>Point</code>
794 */
795
796 protected void updateAutoscroll(Point dragCursorLocn) {
797 if (autoScroller != null) autoScroller.updateLocation(dragCursorLocn);
798 }
799
800 /**
801 * clear autoscrolling
802 */
803
804 protected void clearAutoscroll() {
805 if (autoScroller != null) {
806 autoScroller.stop();
807 autoScroller = null;
808 }
809 }
810
811 /**
812 * The DropTargetContext associated with this DropTarget.
813 *
826 * That Component's Peer
827 */
828 private transient ComponentPeer componentPeer;
829
830 /*
831 * That Component's "native" Peer
832 */
833 private transient DropTargetPeer nativePeer;
834
835
836 /**
837 * Default permissible actions supported by this DropTarget.
838 *
839 * @see #setDefaultActions
840 * @see #getDefaultActions
841 * @serial
842 */
843 int actions = DnDConstants.ACTION_COPY_OR_MOVE;
844
845 /**
846 * <code>true</code> if the DropTarget is accepting Drag & Drop operations.
847 *
848 * @serial
849 */
850 boolean active = true;
851
852 /*
853 * the auto scrolling object
854 */
855
856 private transient DropTargetAutoScroller autoScroller;
857
858 /*
859 * The delegate
860 */
861
862 private transient DropTargetListener dtListener;
863
864 /*
865 * The FlavorMap
866 */
|
37 import java.awt.GraphicsEnvironment;
38 import java.awt.HeadlessException;
39 import java.awt.Insets;
40 import java.awt.Point;
41 import java.awt.Rectangle;
42 import java.awt.Toolkit;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.datatransfer.FlavorMap;
46 import java.awt.datatransfer.SystemFlavorMap;
47 import javax.swing.Timer;
48 import java.awt.peer.ComponentPeer;
49 import java.awt.peer.LightweightPeer;
50 import java.awt.dnd.peer.DropTargetPeer;
51
52 import sun.awt.AWTAccessor;
53 import sun.awt.AWTAccessor.ComponentAccessor;
54
55
56 /**
57 * The {@code DropTarget} is associated
58 * with a {@code Component} when that {@code Component}
59 * wishes
60 * to accept drops during Drag and Drop operations.
61 * <P>
62 * Each
63 * {@code DropTarget} is associated with a {@code FlavorMap}.
64 * The default {@code FlavorMap} hereafter designates the
65 * {@code FlavorMap} returned by {@code SystemFlavorMap.getDefaultFlavorMap()}.
66 *
67 * @since 1.2
68 */
69
70 public class DropTarget implements DropTargetListener, Serializable {
71
72 private static final long serialVersionUID = -6283860791671019047L;
73
74 /**
75 * Creates a new DropTarget given the {@code Component}
76 * to associate itself with, an {@code int} representing
77 * the default acceptable action(s) to
78 * support, a {@code DropTargetListener}
79 * to handle event processing, a {@code boolean} indicating
80 * if the {@code DropTarget} is currently accepting drops, and
81 * a {@code FlavorMap} to use (or null for the default {@code FlavorMap}).
82 * <P>
83 * The Component will receive drops only if it is enabled.
84 * @param c The {@code Component} with which this {@code DropTarget} is associated
85 * @param ops The default acceptable actions for this {@code DropTarget}
86 * @param dtl The {@code DropTargetListener} for this {@code DropTarget}
87 * @param act Is the {@code DropTarget} accepting drops.
88 * @param fm The {@code FlavorMap} to use, or null for the default {@code FlavorMap}
89 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
90 * returns true
91 * @see java.awt.GraphicsEnvironment#isHeadless
92 */
93 public DropTarget(Component c, int ops, DropTargetListener dtl,
94 boolean act, FlavorMap fm)
95 throws HeadlessException
96 {
97 if (GraphicsEnvironment.isHeadless()) {
98 throw new HeadlessException();
99 }
100
101 component = c;
102
103 setDefaultActions(ops);
104
105 if (dtl != null) try {
106 addDropTargetListener(dtl);
107 } catch (TooManyListenersException tmle) {
108 // do nothing!
109 }
110
111 if (c != null) {
112 c.setDropTarget(this);
113 setActive(act);
114 }
115
116 if (fm != null) {
117 flavorMap = fm;
118 } else {
119 flavorMap = SystemFlavorMap.getDefaultFlavorMap();
120 }
121 }
122
123 /**
124 * Creates a {@code DropTarget} given the {@code Component}
125 * to associate itself with, an {@code int} representing
126 * the default acceptable action(s)
127 * to support, a {@code DropTargetListener}
128 * to handle event processing, and a {@code boolean} indicating
129 * if the {@code DropTarget} is currently accepting drops.
130 * <P>
131 * The Component will receive drops only if it is enabled.
132 * @param c The {@code Component} with which this {@code DropTarget} is associated
133 * @param ops The default acceptable actions for this {@code DropTarget}
134 * @param dtl The {@code DropTargetListener} for this {@code DropTarget}
135 * @param act Is the {@code DropTarget} accepting drops.
136 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
137 * returns true
138 * @see java.awt.GraphicsEnvironment#isHeadless
139 */
140 public DropTarget(Component c, int ops, DropTargetListener dtl,
141 boolean act)
142 throws HeadlessException
143 {
144 this(c, ops, dtl, act, null);
145 }
146
147 /**
148 * Creates a {@code DropTarget}.
149 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
150 * returns true
151 * @see java.awt.GraphicsEnvironment#isHeadless
152 */
153 public DropTarget() throws HeadlessException {
154 this(null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
155 }
156
157 /**
158 * Creates a {@code DropTarget} given the {@code Component}
159 * to associate itself with, and the {@code DropTargetListener}
160 * to handle event processing.
161 * <P>
162 * The Component will receive drops only if it is enabled.
163 * @param c The {@code Component} with which this {@code DropTarget} is associated
164 * @param dtl The {@code DropTargetListener} for this {@code DropTarget}
165 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
166 * returns true
167 * @see java.awt.GraphicsEnvironment#isHeadless
168 */
169 public DropTarget(Component c, DropTargetListener dtl)
170 throws HeadlessException
171 {
172 this(c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
173 }
174
175 /**
176 * Creates a {@code DropTarget} given the {@code Component}
177 * to associate itself with, an {@code int} representing
178 * the default acceptable action(s) to support, and a
179 * {@code DropTargetListener} to handle event processing.
180 * <P>
181 * The Component will receive drops only if it is enabled.
182 * @param c The {@code Component} with which this {@code DropTarget} is associated
183 * @param ops The default acceptable actions for this {@code DropTarget}
184 * @param dtl The {@code DropTargetListener} for this {@code DropTarget}
185 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
186 * returns true
187 * @see java.awt.GraphicsEnvironment#isHeadless
188 */
189 public DropTarget(Component c, int ops, DropTargetListener dtl)
190 throws HeadlessException
191 {
192 this(c, ops, dtl, true);
193 }
194
195 /**
196 * Note: this interface is required to permit the safe association
197 * of a DropTarget with a Component in one of two ways, either:
198 * {@code component.setDropTarget(droptarget);}
199 * or {@code droptarget.setComponent(component);}
200 * <P>
201 * The Component will receive drops only if it is enabled.
202 * @param c The new {@code Component} this {@code DropTarget}
203 * is to be associated with.
204 */
205
206 public synchronized void setComponent(Component c) {
207 if (component == c || component != null && component.equals(c))
208 return;
209
210 final Component old = component;
211
212 if (old != null) {
213 clearAutoscroll();
214
215 component = null;
216 removeNotify();
217 old.setDropTarget(null);
218
219 }
220
221 if ((component = c) != null) try {
222 c.setDropTarget(this);
223 } catch (Exception e) { // undo the change
224 if (old != null) {
225 old.setDropTarget(this);
226 addNotify();
227 }
228 }
229 }
230
231 /**
232 * Gets the {@code Component} associated
233 * with this {@code DropTarget}.
234 *
235 * @return the current {@code Component}
236 */
237
238 public synchronized Component getComponent() {
239 return component;
240 }
241
242 /**
243 * Sets the default acceptable actions for this {@code DropTarget}
244 *
245 * @param ops the default actions
246 * @see java.awt.dnd.DnDConstants
247 */
248
249 public void setDefaultActions(int ops) {
250 getDropTargetContext().setTargetActions(ops & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE));
251 }
252
253 /*
254 * Called by DropTargetContext.setTargetActions()
255 * with appropriate synchronization.
256 */
257 void doSetDefaultActions(int ops) {
258 actions = ops;
259 }
260
261 /**
262 * Gets an {@code int} representing the
263 * current action(s) supported by this {@code DropTarget}.
264 *
265 * @return the current default actions
266 */
267
268 public int getDefaultActions() {
269 return actions;
270 }
271
272 /**
273 * Sets the DropTarget active if {@code true},
274 * inactive if {@code false}.
275 *
276 * @param isActive sets the {@code DropTarget} (in)active.
277 */
278
279 public synchronized void setActive(boolean isActive) {
280 if (isActive != active) {
281 active = isActive;
282 }
283
284 if (!active) clearAutoscroll();
285 }
286
287 /**
288 * Reports whether or not
289 * this {@code DropTarget}
290 * is currently active (ready to accept drops).
291 *
292 * @return {@code true} if active, {@code false} if not
293 */
294
295 public boolean isActive() {
296 return active;
297 }
298
299 /**
300 * Adds a new {@code DropTargetListener} (UNICAST SOURCE).
301 *
302 * @param dtl The new {@code DropTargetListener}
303 *
304 * @throws TooManyListenersException if a
305 * {@code DropTargetListener} is already added to this
306 * {@code DropTarget}.
307 */
308
309 public synchronized void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
310 if (dtl == null) return;
311
312 if (equals(dtl)) throw new IllegalArgumentException("DropTarget may not be its own Listener");
313
314 if (dtListener == null)
315 dtListener = dtl;
316 else
317 throw new TooManyListenersException();
318 }
319
320 /**
321 * Removes the current {@code DropTargetListener} (UNICAST SOURCE).
322 *
323 * @param dtl the DropTargetListener to deregister.
324 */
325
326 public synchronized void removeDropTargetListener(DropTargetListener dtl) {
327 if (dtl != null && dtListener != null) {
328 if(dtListener.equals(dtl))
329 dtListener = null;
330 else
331 throw new IllegalArgumentException("listener mismatch");
332 }
333 }
334
335 /**
336 * Calls {@code dragEnter} on the registered
337 * {@code DropTargetListener} and passes it
338 * the specified {@code DropTargetDragEvent}.
339 * Has no effect if this {@code DropTarget}
340 * is not active.
341 *
342 * @param dtde the {@code DropTargetDragEvent}
343 *
344 * @throws NullPointerException if this {@code DropTarget}
345 * is active and {@code dtde} is {@code null}
346 *
347 * @see #isActive
348 */
349 public synchronized void dragEnter(DropTargetDragEvent dtde) {
350 isDraggingInside = true;
351
352 if (!active) return;
353
354 if (dtListener != null) {
355 dtListener.dragEnter(dtde);
356 } else
357 dtde.getDropTargetContext().setTargetActions(DnDConstants.ACTION_NONE);
358
359 initializeAutoscrolling(dtde.getLocation());
360 }
361
362 /**
363 * Calls {@code dragOver} on the registered
364 * {@code DropTargetListener} and passes it
365 * the specified {@code DropTargetDragEvent}.
366 * Has no effect if this {@code DropTarget}
367 * is not active.
368 *
369 * @param dtde the {@code DropTargetDragEvent}
370 *
371 * @throws NullPointerException if this {@code DropTarget}
372 * is active and {@code dtde} is {@code null}
373 *
374 * @see #isActive
375 */
376 public synchronized void dragOver(DropTargetDragEvent dtde) {
377 if (!active) return;
378
379 if (dtListener != null && active) dtListener.dragOver(dtde);
380
381 updateAutoscroll(dtde.getLocation());
382 }
383
384 /**
385 * Calls {@code dropActionChanged} on the registered
386 * {@code DropTargetListener} and passes it
387 * the specified {@code DropTargetDragEvent}.
388 * Has no effect if this {@code DropTarget}
389 * is not active.
390 *
391 * @param dtde the {@code DropTargetDragEvent}
392 *
393 * @throws NullPointerException if this {@code DropTarget}
394 * is active and {@code dtde} is {@code null}
395 *
396 * @see #isActive
397 */
398 public synchronized void dropActionChanged(DropTargetDragEvent dtde) {
399 if (!active) return;
400
401 if (dtListener != null) dtListener.dropActionChanged(dtde);
402
403 updateAutoscroll(dtde.getLocation());
404 }
405
406 /**
407 * Calls {@code dragExit} on the registered
408 * {@code DropTargetListener} and passes it
409 * the specified {@code DropTargetEvent}.
410 * Has no effect if this {@code DropTarget}
411 * is not active.
412 * <p>
413 * This method itself does not throw any exception
414 * for null parameter but for exceptions thrown by
415 * the respective method of the listener.
416 *
417 * @param dte the {@code DropTargetEvent}
418 *
419 * @see #isActive
420 */
421 public synchronized void dragExit(DropTargetEvent dte) {
422 isDraggingInside = false;
423
424 if (!active) return;
425
426 if (dtListener != null && active) dtListener.dragExit(dte);
427
428 clearAutoscroll();
429 }
430
431 /**
432 * Calls {@code drop} on the registered
433 * {@code DropTargetListener} and passes it
434 * the specified {@code DropTargetDropEvent}
435 * if this {@code DropTarget} is active.
436 *
437 * @param dtde the {@code DropTargetDropEvent}
438 *
439 * @throws NullPointerException if {@code dtde} is null
440 * and at least one of the following is true: this
441 * {@code DropTarget} is not active, or there is
442 * no a {@code DropTargetListener} registered.
443 *
444 * @see #isActive
445 */
446 public synchronized void drop(DropTargetDropEvent dtde) {
447 isDraggingInside = false;
448
449 clearAutoscroll();
450
451 if (dtListener != null && active)
452 dtListener.drop(dtde);
453 else { // we shouldn't get here ...
454 dtde.rejectDrop();
455 }
456 }
457
458 /**
459 * Gets the {@code FlavorMap}
460 * associated with this {@code DropTarget}.
461 * If no {@code FlavorMap} has been set for this
462 * {@code DropTarget}, it is associated with the default
463 * {@code FlavorMap}.
464 *
465 * @return the FlavorMap for this DropTarget
466 */
467
468 public FlavorMap getFlavorMap() { return flavorMap; }
469
470 /**
471 * Sets the {@code FlavorMap} associated
472 * with this {@code DropTarget}.
473 *
474 * @param fm the new {@code FlavorMap}, or null to
475 * associate the default FlavorMap with this DropTarget.
476 */
477
478 public void setFlavorMap(FlavorMap fm) {
479 flavorMap = fm == null ? SystemFlavorMap.getDefaultFlavorMap() : fm;
480 }
481
482 /**
483 * Notify the DropTarget that it has been associated with a Component
484 *
485 **********************************************************************
486 * This method is usually called from java.awt.Component.addNotify() of
487 * the Component associated with this DropTarget to notify the DropTarget
488 * that a ComponentPeer has been associated with that Component.
489 *
490 * Calling this method, other than to notify this DropTarget of the
491 * association of the ComponentPeer with the Component may result in
492 * a malfunction of the DnD system.
493 **********************************************************************
494 */
527 * disassociation of the ComponentPeer from the Component may result in
528 * a malfunction of the DnD system.
529 **********************************************************************
530 */
531
532 public void removeNotify() {
533 if (nativePeer != null) {
534 nativePeer.removeDropTarget(this);
535 }
536 componentPeer = null;
537 nativePeer = null;
538
539 synchronized (this) {
540 if (isDraggingInside) {
541 dragExit(new DropTargetEvent(getDropTargetContext()));
542 }
543 }
544 }
545
546 /**
547 * Gets the {@code DropTargetContext} associated
548 * with this {@code DropTarget}.
549 *
550 * @return the {@code DropTargetContext} associated with this {@code DropTarget}.
551 */
552
553 public DropTargetContext getDropTargetContext() {
554 return dropTargetContext;
555 }
556
557 /**
558 * Creates the DropTargetContext associated with this DropTarget.
559 * Subclasses may override this method to instantiate their own
560 * DropTargetContext subclass.
561 *
562 * This call is typically *only* called by the platform's
563 * DropTargetContextPeer as a drag operation encounters this
564 * DropTarget. Accessing the Context while no Drag is current
565 * has undefined results.
566 * @return the DropTargetContext associated with this DropTarget
567 */
568
569 protected DropTargetContext createDropTargetContext() {
570 return new DropTargetContext(this);
571 }
572
573 /**
574 * Serializes this {@code DropTarget}. Performs default serialization,
575 * and then writes out this object's {@code DropTargetListener} if and
576 * only if it can be serialized. If not, {@code null} is written
577 * instead.
578 *
579 * @serialData The default serializable fields, in alphabetical order,
580 * followed by either a {@code DropTargetListener}
581 * instance, or {@code null}.
582 * @since 1.4
583 */
584 private void writeObject(ObjectOutputStream s) throws IOException {
585 s.defaultWriteObject();
586
587 s.writeObject(SerializationTester.test(dtListener)
588 ? dtListener : null);
589 }
590
591 /**
592 * Deserializes this {@code DropTarget}. This method first performs
593 * default deserialization for all non-{@code transient} fields. An
594 * attempt is then made to deserialize this object's
595 * {@code DropTargetListener} as well. This is first attempted by
596 * deserializing the field {@code dtListener}, because, in releases
597 * prior to 1.4, a non-{@code transient} field of this name stored the
598 * {@code DropTargetListener}. If this fails, the next object in the
599 * stream is used instead.
600 *
601 * @since 1.4
602 */
603 private void readObject(ObjectInputStream s)
604 throws ClassNotFoundException, IOException
605 {
606 ObjectInputStream.GetField f = s.readFields();
607
608 try {
609 dropTargetContext =
610 (DropTargetContext)f.get("dropTargetContext", null);
611 } catch (IllegalArgumentException e) {
612 // Pre-1.4 support. 'dropTargetContext' was previously transient
613 }
614 if (dropTargetContext == null) {
615 dropTargetContext = createDropTargetContext();
616 }
617
618 component = (Component)f.get("component", null);
622 // Pre-1.4 support. 'dtListener' was previously non-transient
623 try {
624 dtListener = (DropTargetListener)f.get("dtListener", null);
625 } catch (IllegalArgumentException e) {
626 // 1.4-compatible byte stream. 'dtListener' was written explicitly
627 dtListener = (DropTargetListener)s.readObject();
628 }
629 }
630
631 /*********************************************************************/
632
633 /**
634 * this protected nested class implements autoscrolling
635 */
636
637 protected static class DropTargetAutoScroller implements ActionListener {
638
639 /**
640 * construct a DropTargetAutoScroller
641 *
642 * @param c the {@code Component}
643 * @param p the {@code Point}
644 */
645
646 protected DropTargetAutoScroller(Component c, Point p) {
647 super();
648
649 component = c;
650 autoScroll = (Autoscroll)component;
651
652 Toolkit t = Toolkit.getDefaultToolkit();
653
654 Integer initial = Integer.valueOf(100);
655 Integer interval = Integer.valueOf(100);
656
657 try {
658 initial = (Integer)t.getDesktopProperty("DnD.Autoscroll.initialDelay");
659 } catch (Exception e) {
660 // ignore
661 }
662
663 try {
692 Insets i = autoScroll.getAutoscrollInsets();
693 Dimension size = component.getSize();
694
695 if (size.width != outer.width || size.height != outer.height)
696 outer.reshape(0, 0, size.width, size.height);
697
698 if (inner.x != i.left || inner.y != i.top)
699 inner.setLocation(i.left, i.top);
700
701 int newWidth = size.width - (i.left + i.right);
702 int newHeight = size.height - (i.top + i.bottom);
703
704 if (newWidth != inner.width || newHeight != inner.height)
705 inner.setSize(newWidth, newHeight);
706
707 }
708
709 /**
710 * cause autoscroll to occur
711 *
712 * @param newLocn the {@code Point}
713 */
714
715 protected synchronized void updateLocation(Point newLocn) {
716 prev = locn;
717 locn = newLocn;
718
719 if (Math.abs(locn.x - prev.x) > hysteresis ||
720 Math.abs(locn.y - prev.y) > hysteresis) {
721 if (timer.isRunning()) timer.stop();
722 } else {
723 if (!timer.isRunning()) timer.start();
724 }
725 }
726
727 /**
728 * cause autoscrolling to stop
729 */
730
731 protected void stop() { timer.stop(); }
732
733 /**
734 * cause autoscroll to occur
735 *
736 * @param e the {@code ActionEvent}
737 */
738
739 public synchronized void actionPerformed(ActionEvent e) {
740 updateRegion();
741
742 if (outer.contains(locn) && !inner.contains(locn))
743 autoScroll.autoscroll(locn);
744 }
745
746 /*
747 * fields
748 */
749
750 private Component component;
751 private Autoscroll autoScroll;
752
753 private Timer timer;
754
755 private Point locn;
756 private Point prev;
757
758 private Rectangle outer = new Rectangle();
759 private Rectangle inner = new Rectangle();
760
761 private int hysteresis = 10;
762 }
763
764 /*********************************************************************/
765
766 /**
767 * create an embedded autoscroller
768 *
769 * @param c the {@code Component}
770 * @param p the {@code Point}
771 * @return an embedded autoscroller
772 */
773
774 protected DropTargetAutoScroller createDropTargetAutoScroller(Component c, Point p) {
775 return new DropTargetAutoScroller(c, p);
776 }
777
778 /**
779 * initialize autoscrolling
780 *
781 * @param p the {@code Point}
782 */
783
784 protected void initializeAutoscrolling(Point p) {
785 if (component == null || !(component instanceof Autoscroll)) return;
786
787 autoScroller = createDropTargetAutoScroller(component, p);
788 }
789
790 /**
791 * update autoscrolling with current cursor location
792 *
793 * @param dragCursorLocn the {@code Point}
794 */
795
796 protected void updateAutoscroll(Point dragCursorLocn) {
797 if (autoScroller != null) autoScroller.updateLocation(dragCursorLocn);
798 }
799
800 /**
801 * clear autoscrolling
802 */
803
804 protected void clearAutoscroll() {
805 if (autoScroller != null) {
806 autoScroller.stop();
807 autoScroller = null;
808 }
809 }
810
811 /**
812 * The DropTargetContext associated with this DropTarget.
813 *
826 * That Component's Peer
827 */
828 private transient ComponentPeer componentPeer;
829
830 /*
831 * That Component's "native" Peer
832 */
833 private transient DropTargetPeer nativePeer;
834
835
836 /**
837 * Default permissible actions supported by this DropTarget.
838 *
839 * @see #setDefaultActions
840 * @see #getDefaultActions
841 * @serial
842 */
843 int actions = DnDConstants.ACTION_COPY_OR_MOVE;
844
845 /**
846 * {@code true} if the DropTarget is accepting Drag & Drop operations.
847 *
848 * @serial
849 */
850 boolean active = true;
851
852 /*
853 * the auto scrolling object
854 */
855
856 private transient DropTargetAutoScroller autoScroller;
857
858 /*
859 * The delegate
860 */
861
862 private transient DropTargetListener dtListener;
863
864 /*
865 * The FlavorMap
866 */
|