1. /*
  2. * @(#)DragSourceContext.java 1.50 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt.dnd;
  8. import java.awt.event.InputEvent;
  9. import java.awt.Component;
  10. import java.awt.Cursor;
  11. import java.awt.Image;
  12. import java.awt.Point;
  13. import java.awt.datatransfer.DataFlavor;
  14. import java.awt.datatransfer.Transferable;
  15. import java.awt.datatransfer.UnsupportedFlavorException;
  16. import java.awt.dnd.DragGestureEvent;
  17. import java.awt.dnd.DragSource;
  18. import java.awt.dnd.DragSourceListener;
  19. import java.awt.dnd.InvalidDnDOperationException;
  20. import java.awt.dnd.peer.DragSourceContextPeer;
  21. import java.io.IOException;
  22. import java.io.ObjectOutputStream;
  23. import java.io.ObjectInputStream;
  24. import java.io.Serializable;
  25. import java.util.TooManyListenersException;
  26. /**
  27. * The <code>DragSourceContext</code> class is responsible for managing the
  28. * initiator side of the Drag and Drop protocol. In particular, it is responsible
  29. * for managing drag event notifications to the <code>DragSourceListener</code>s
  30. * and <code>DragSourceMotionListener</code>s, and providing the
  31. * <code>Transferable</code> representing the source data for the drag operation.
  32. * <p>
  33. * Note that the <code>DragSourceContext</code> itself
  34. * implements the <code>DragSourceListener</code> and
  35. * <code>DragSourceMotionListener</code> interfaces.
  36. * This is to allow the platform peer
  37. * (the <code>DragSourceContextPeer</code> instance)
  38. * created by the <code>DragSource</code> to notify
  39. * the <code>DragSourceContext</code> of
  40. * state changes in the ongoing operation. This allows the
  41. * <code>DragSourceContext</code> to interpose
  42. * itself between the platform and the
  43. * listeners provided by the initiator of the drag operation.
  44. *
  45. * @see DragSourceListener
  46. * @see DragSourceMotionListener
  47. * @version 1.50, 01/23/03
  48. * @since 1.2
  49. */
  50. public class DragSourceContext
  51. implements DragSourceListener, DragSourceMotionListener, Serializable {
  52. private static final long serialVersionUID = -115407898692194719L;
  53. // used by updateCurrentCursor
  54. /**
  55. * An <code>int</code> used by updateCurrentCursor()
  56. * indicating that the <code>Cursor</code> should change
  57. * to the default (no drop) <code>Cursor</code>.
  58. */
  59. protected static final int DEFAULT = 0;
  60. /**
  61. * An <code>int</code> used by updateCurrentCursor()
  62. * indicating that the <code>Cursor</code>
  63. * has entered a <code>DropTarget</code>.
  64. */
  65. protected static final int ENTER = 1;
  66. /**
  67. * An <code>int</code> used by updateCurrentCursor()
  68. * indicating that the <code>Cursor</code> is
  69. * over a <code>DropTarget</code>.
  70. */
  71. protected static final int OVER = 2;
  72. /**
  73. * An <code>int</code> used by updateCurrentCursor()
  74. * indicating that the user operation has changed.
  75. */
  76. protected static final int CHANGED = 3;
  77. /**
  78. * Called from <code>DragSource</code>, this constructor creates a new
  79. * <code>DragSourceContext</code> given the
  80. * <code>DragSourceContextPeer</code> for this Drag, the
  81. * <code>DragGestureEvent</code> that triggered the Drag, the initial
  82. * <code>Cursor</code> to use for the Drag, an (optional)
  83. * <code>Image</code> to display while the Drag is taking place, the offset
  84. * of the <code>Image</code> origin from the hotspot at the instant of the
  85. * triggering event, the <code>Transferable</code> subject data, and the
  86. * <code>DragSourceListener</code> to use during the Drag and Drop
  87. * operation.
  88. * <br>
  89. * If <code>DragSourceContextPeer</code> is <code>null</code>
  90. * <code>NullPointerException</code> is thrown.
  91. * <br>
  92. * If <code>DragGestureEvent</code> is <code>null</code>
  93. * <code>NullPointerException</code> is thrown.
  94. * <br>
  95. * If <code>Cursor</code> is <code>null</code> no exception is thrown and
  96. * the default drag cursor behavior is activated for this drag operation.
  97. * <br>
  98. * If <code>Image</code> is <code>null</code> no exception is thrown.
  99. * <br>
  100. * If <code>Image</code> is not <code>null</code> and the offset is
  101. * <code>null</code> <code>NullPointerException</code> is thrown.
  102. * <br>
  103. * If <code>Transferable</code> is <code>null</code>
  104. * <code>NullPointerException</code> is thrown.
  105. * <br>
  106. * If <code>DragSourceListener</code> is <code>null</code> no exception
  107. * is thrown.
  108. *
  109. * @param dscp the <code>DragSourceContextPeer</code> for this drag
  110. * @param trigger the triggering event
  111. * @param dragCursor the initial <code>Cursor</code>
  112. * @param dragImage the <code>Image</code> to drag (or <code>null</code>)
  113. * @param offset the offset of the image origin from the hotspot at the
  114. * instant of the triggering event
  115. * @param t the <code>Transferable</code>
  116. * @param dsl the <code>DragSourceListener</code>
  117. *
  118. * @throws IllegalArgumentException if the <code>Component</code> associated
  119. * with the trigger event is <code>null</code>.
  120. * @throws IllegalArgumentException if the <code>DragSource</code> for the
  121. * trigger event is <code>null</code>.
  122. * @throws IllegalArgumentException if the drag action for the
  123. * trigger event is <code>DnDConstants.ACTION_NONE</code>.
  124. * @throws IllegalArgumentException if the source actions for the
  125. * <code>DragGestureRecognizer</code> associated with the trigger
  126. * event are equal to <code>DnDConstants.ACTION_NONE</code>.
  127. * @throws NullPointerException if dscp, trigger, or t are null, or
  128. * if dragImage is non-null and offset is null
  129. */
  130. public DragSourceContext(DragSourceContextPeer dscp,
  131. DragGestureEvent trigger, Cursor dragCursor,
  132. Image dragImage, Point offset, Transferable t,
  133. DragSourceListener dsl) {
  134. if (dscp == null) {
  135. throw new NullPointerException("DragSourceContextPeer");
  136. }
  137. if (trigger == null) {
  138. throw new NullPointerException("Trigger");
  139. }
  140. if (trigger.getDragSource() == null) {
  141. throw new IllegalArgumentException("DragSource");
  142. }
  143. if (trigger.getComponent() == null) {
  144. throw new IllegalArgumentException("Component");
  145. }
  146. if (trigger.getSourceAsDragGestureRecognizer().getSourceActions() ==
  147. DnDConstants.ACTION_NONE) {
  148. throw new IllegalArgumentException("source actions");
  149. }
  150. if (trigger.getDragAction() == DnDConstants.ACTION_NONE) {
  151. throw new IllegalArgumentException("no drag action");
  152. }
  153. if (t == null) {
  154. throw new NullPointerException("Transferable");
  155. }
  156. if (dragImage != null && offset == null) {
  157. throw new NullPointerException("offset");
  158. }
  159. peer = dscp;
  160. this.trigger = trigger;
  161. cursor = dragCursor;
  162. transferable = t;
  163. listener = dsl;
  164. sourceActions =
  165. trigger.getSourceAsDragGestureRecognizer().getSourceActions();
  166. useCustomCursor = (dragCursor != null);
  167. updateCurrentCursor(trigger.getDragAction(), getSourceActions(), DEFAULT);
  168. }
  169. /**
  170. * Returns the <code>DragSource</code>
  171. * that instantiated this <code>DragSourceContext</code>.
  172. *
  173. * @return the <code>DragSource</code> that
  174. * instantiated this <code>DragSourceContext</code>
  175. */
  176. public DragSource getDragSource() { return trigger.getDragSource(); }
  177. /**
  178. * Returns the <code>Component</code> associated with this
  179. * <code>DragSourceContext</code>.
  180. *
  181. * @return the <code>Component</code> that started the drag
  182. */
  183. public Component getComponent() { return trigger.getComponent(); }
  184. /**
  185. * Returns the <code>DragGestureEvent</code>
  186. * that initially triggered the drag.
  187. *
  188. * @return the Event that triggered the drag
  189. */
  190. public DragGestureEvent getTrigger() { return trigger; }
  191. /**
  192. * Returns a bitwise mask of <code>DnDConstants</code> that
  193. * represent the set of drop actions supported by the drag source for the
  194. * drag operation associated with this <code>DragSourceContext</code>.
  195. *
  196. * @return the drop actions supported by the drag source
  197. */
  198. public int getSourceActions() {
  199. return sourceActions;
  200. }
  201. /**
  202. * Sets the cursor for this drag operation to the specified
  203. * <code>Cursor</code>. If the specified <code>Cursor</code>
  204. * is <code>null</code>, the default drag cursor behavior is
  205. * activated for this drag operation, otherwise it is deactivated.
  206. *
  207. * @param c the <code>Cursor</code> to display, or
  208. * <code>null</code> to activate the default drag cursor
  209. * behavior
  210. *
  211. */
  212. public synchronized void setCursor(Cursor c) {
  213. useCustomCursor = (c != null);
  214. setCursorImpl(c);
  215. }
  216. /**
  217. * Returns the current drag <code>Cursor</code>.
  218. * <P>
  219. * @return the current drag <code>Cursor</code>
  220. */
  221. public Cursor getCursor() { return cursor; }
  222. /**
  223. * Add a <code>DragSourceListener</code> to this
  224. * <code>DragSourceContext</code> if one has not already been added.
  225. * If a <code>DragSourceListener</code> already exists,
  226. * this method throws a <code>TooManyListenersException</code>.
  227. * <P>
  228. * @param dsl the <code>DragSourceListener</code> to add.
  229. * Note that while <code>null</code> is not prohibited,
  230. * it is not acceptable as a parameter.
  231. * <P>
  232. * @throws <code>TooManyListenersException</code> if
  233. * a <code>DragSourceListener</code> has already been added
  234. */
  235. public synchronized void addDragSourceListener(DragSourceListener dsl) throws TooManyListenersException {
  236. if (dsl == null) return;
  237. if (equals(dsl)) throw new IllegalArgumentException("DragSourceContext may not be its own listener");
  238. if (listener != null)
  239. throw new TooManyListenersException();
  240. else
  241. listener = dsl;
  242. }
  243. /**
  244. * Removes the specified <code>DragSourceListener</code>
  245. * from this <code>DragSourceContext</code>.
  246. *
  247. * @param dsl the <code>DragSourceListener</code> to remove;
  248. * note that while <code>null</code> is not prohibited,
  249. * it is not acceptable as a parameter
  250. */
  251. public synchronized void removeDragSourceListener(DragSourceListener dsl) {
  252. if (listener != null && listener.equals(dsl)) {
  253. listener = null;
  254. } else
  255. throw new IllegalArgumentException();
  256. }
  257. /**
  258. * Notifies the peer that the <code>Transferable</code>'s
  259. * <code>DataFlavor</code>s have changed.
  260. */
  261. public void transferablesFlavorsChanged() {
  262. if (peer != null) peer.transferablesFlavorsChanged();
  263. }
  264. /**
  265. * Calls <code>dragEnter</code> on the
  266. * <code>DragSourceListener</code>s registered with this
  267. * <code>DragSourceContext</code> and with the associated
  268. * <code>DragSource</code>, and passes them the specified
  269. * <code>DragSourceDragEvent</code>.
  270. *
  271. * @param dsde the <code>DragSourceDragEvent</code>
  272. */
  273. public void dragEnter(DragSourceDragEvent dsde) {
  274. DragSourceListener dsl = listener;
  275. if (dsl != null) {
  276. dsl.dragEnter(dsde);
  277. }
  278. getDragSource().processDragEnter(dsde);
  279. updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), ENTER);
  280. }
  281. /**
  282. * Calls <code>dragOver</code> on the
  283. * <code>DragSourceListener</code>s registered with this
  284. * <code>DragSourceContext</code> and with the associated
  285. * <code>DragSource</code>, and passes them the specified
  286. * <code>DragSourceDragEvent</code>.
  287. *
  288. * @param dsde the <code>DragSourceDragEvent</code>
  289. */
  290. public void dragOver(DragSourceDragEvent dsde) {
  291. DragSourceListener dsl = listener;
  292. if (dsl != null) {
  293. dsl.dragOver(dsde);
  294. }
  295. getDragSource().processDragOver(dsde);
  296. updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), OVER);
  297. }
  298. /**
  299. * Calls <code>dragExit</code> on the
  300. * <code>DragSourceListener</code>s registered with this
  301. * <code>DragSourceContext</code> and with the associated
  302. * <code>DragSource</code>, and passes them the specified
  303. * <code>DragSourceEvent</code>.
  304. *
  305. * @param dse the <code>DragSourceEvent</code>
  306. */
  307. public void dragExit(DragSourceEvent dse) {
  308. DragSourceListener dsl = listener;
  309. if (dsl != null) {
  310. dsl.dragExit(dse);
  311. }
  312. getDragSource().processDragExit(dse);
  313. updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE, DEFAULT);
  314. }
  315. /**
  316. * Calls <code>dropActionChanged</code> on the
  317. * <code>DragSourceListener</code>s registered with this
  318. * <code>DragSourceContext</code> and with the associated
  319. * <code>DragSource</code>, and passes them the specified
  320. * <code>DragSourceDragEvent</code>.
  321. *
  322. * @param dsde the <code>DragSourceDragEvent</code>
  323. */
  324. public void dropActionChanged(DragSourceDragEvent dsde) {
  325. DragSourceListener dsl = listener;
  326. if (dsl != null) {
  327. dsl.dropActionChanged(dsde);
  328. }
  329. getDragSource().processDropActionChanged(dsde);
  330. updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), CHANGED);
  331. }
  332. /**
  333. * Calls <code>dragDropEnd</code> on the
  334. * <code>DragSourceListener</code>s registered with this
  335. * <code>DragSourceContext</code> and with the associated
  336. * <code>DragSource</code>, and passes them the specified
  337. * <code>DragSourceDropEvent</code>.
  338. *
  339. * @param dsde the <code>DragSourceDropEvent</code>
  340. */
  341. public void dragDropEnd(DragSourceDropEvent dsde) {
  342. DragSourceListener dsl = listener;
  343. if (dsl != null) {
  344. dsl.dragDropEnd(dsde);
  345. }
  346. getDragSource().processDragDropEnd(dsde);
  347. }
  348. /**
  349. * Calls <code>dragMouseMoved</code> on the
  350. * <code>DragSourceMotionListener</code>s registered with the
  351. * <code>DragSource</code> associated with this
  352. * <code>DragSourceContext</code>, and them passes the specified
  353. * <code>DragSourceDragEvent</code>.
  354. *
  355. * @param dsde the <code>DragSourceDragEvent</code>
  356. * @since 1.4
  357. */
  358. public void dragMouseMoved(DragSourceDragEvent dsde) {
  359. getDragSource().processDragMouseMoved(dsde);
  360. }
  361. /**
  362. * Returns the <code>Transferable</code> associated with
  363. * this <code>DragSourceContext</code>.
  364. *
  365. * @return the <code>Transferable</code>
  366. */
  367. public Transferable getTransferable() { return transferable; }
  368. /**
  369. * If the default drag cursor behavior is active, this method
  370. * sets the default drag cursor for the specified selected
  371. * operation, supported actions and status, otherwise this
  372. * method does nothing.
  373. *
  374. * @param dropOp the user's currently selected operation
  375. * @param targetAct the current target's supported actions
  376. * @param status the constant
  377. */
  378. protected synchronized void updateCurrentCursor(int dropOp, int targetAct, int status) {
  379. // if the cursor has been previously set then dont do any defaults
  380. // processing.
  381. if (useCustomCursor) {
  382. return;
  383. }
  384. // do defaults processing
  385. Cursor c = null;
  386. switch (status) {
  387. default:
  388. targetAct = DnDConstants.ACTION_NONE;
  389. case ENTER:
  390. case OVER:
  391. case CHANGED:
  392. int ra = dropOp & targetAct;
  393. if (ra == DnDConstants.ACTION_NONE) { // no drop possible
  394. if ((dropOp & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
  395. c = DragSource.DefaultLinkNoDrop;
  396. else if ((dropOp & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
  397. c = DragSource.DefaultMoveNoDrop;
  398. else
  399. c = DragSource.DefaultCopyNoDrop;
  400. } else { // drop possible
  401. if ((ra & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
  402. c = DragSource.DefaultLinkDrop;
  403. else if ((ra & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
  404. c = DragSource.DefaultMoveDrop;
  405. else
  406. c = DragSource.DefaultCopyDrop;
  407. }
  408. }
  409. setCursorImpl(c);
  410. }
  411. private void setCursorImpl(Cursor c) {
  412. if (cursor == null || !cursor.equals(c)) {
  413. cursor = c;
  414. if (peer != null) peer.setCursor(cursor);
  415. }
  416. }
  417. /**
  418. * Serializes this <code>DragSourceContext</code>. This method first
  419. * performs default serialization. Next, this object's
  420. * <code>Transferable</code> is written out if and only if it can be
  421. * serialized. If not, <code>null</code> is written instead. In this case,
  422. * a <code>DragSourceContext</code> created from the resulting deserialized
  423. * stream will contain a dummy <code>Transferable</code> which supports no
  424. * <code>DataFlavor</code>s. Finally, this object's
  425. * <code>DragSourceListener</code> is written out if and only if it can be
  426. * serialized. If not, <code>null</code> is written instead.
  427. *
  428. * @serialData The default serializable fields, in alphabetical order,
  429. * followed by either a <code>Transferable</code> instance, or
  430. * <code>null</code>, followed by either a
  431. * <code>DragSourceListener</code> instance, or
  432. * <code>null</code>.
  433. * @since 1.4
  434. */
  435. private void writeObject(ObjectOutputStream s) throws IOException {
  436. s.defaultWriteObject();
  437. s.writeObject(SerializationTester.test(transferable)
  438. ? transferable : null);
  439. s.writeObject(SerializationTester.test(listener)
  440. ? listener : null);
  441. }
  442. /**
  443. * Deserializes this <code>DragSourceContext</code>. This method first
  444. * performs default deserialization for all non-<code>transient</code>
  445. * fields. This object's <code>Transferable</code> and
  446. * <code>DragSourceListener</code> are then deserialized as well by using
  447. * the next two objects in the stream. If the resulting
  448. * <code>Transferable</code> is <code>null</code>, this object's
  449. * <code>Transferable</code> is set to a dummy <code>Transferable</code>
  450. * which supports no <code>DataFlavor</code>s.
  451. *
  452. * @since 1.4
  453. */
  454. private void readObject(ObjectInputStream s)
  455. throws ClassNotFoundException, IOException
  456. {
  457. s.defaultReadObject();
  458. transferable = (Transferable)s.readObject();
  459. listener = (DragSourceListener)s.readObject();
  460. // Implementation assumes 'transferable' is never null.
  461. if (transferable == null) {
  462. if (emptyTransferable == null) {
  463. emptyTransferable = new Transferable() {
  464. public DataFlavor[] getTransferDataFlavors() {
  465. return new DataFlavor[0];
  466. }
  467. public boolean isDataFlavorSupported(DataFlavor flavor)
  468. {
  469. return false;
  470. }
  471. public Object getTransferData(DataFlavor flavor)
  472. throws UnsupportedFlavorException
  473. {
  474. throw new UnsupportedFlavorException(flavor);
  475. }
  476. };
  477. }
  478. transferable = emptyTransferable;
  479. }
  480. }
  481. private static Transferable emptyTransferable;
  482. /*
  483. * fields
  484. */
  485. private transient DragSourceContextPeer peer;
  486. /**
  487. * The event which triggered the start of the drag.
  488. *
  489. * @serial
  490. */
  491. private DragGestureEvent trigger;
  492. /**
  493. * The current drag cursor.
  494. *
  495. * @serial
  496. */
  497. private Cursor cursor;
  498. private transient Transferable transferable;
  499. private transient DragSourceListener listener;
  500. /**
  501. * <code>true</code> if the custom drag cursor is used instead of the
  502. * default one.
  503. *
  504. * @serial
  505. */
  506. private boolean useCustomCursor;
  507. /**
  508. * A bitwise mask of <code>DnDConstants</code> that represents the set of
  509. * drop actions supported by the drag source for the drag operation associated
  510. * with this <code>DragSourceContext.</code>
  511. *
  512. * @serial
  513. */
  514. private final int sourceActions;
  515. }