1. /*
  2. * @(#)DragSourceContext.java 1.42 01/04/30
  3. *
  4. * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt.dnd;
  11. import java.awt.event.InputEvent;
  12. import java.awt.Component;
  13. import java.awt.Cursor;
  14. import java.awt.Image;
  15. import java.awt.Point;
  16. import java.awt.datatransfer.DataFlavor;
  17. import java.awt.datatransfer.Transferable;
  18. import java.awt.datatransfer.UnsupportedFlavorException;
  19. import java.awt.dnd.DragGestureEvent;
  20. import java.awt.dnd.DragSource;
  21. import java.awt.dnd.DragSourceListener;
  22. import java.awt.dnd.InvalidDnDOperationException;
  23. import java.awt.dnd.peer.DragSourceContextPeer;
  24. import java.io.IOException;
  25. import java.util.TooManyListenersException;
  26. /**
  27. * The DragSourceContext class is responsible for managing the initiator side
  28. * of the Drag and Drop protocol. In particular it is responsible for managing
  29. * event notifications to the DragSourceListener, and providing the
  30. * Transferable state to enable the data transfer.
  31. * <P>
  32. * An instance of this class is created as a result
  33. * of a <code>DragSource's</code> startDrag() method being successfully
  34. * invoked. This instance is responsible for tracking the state
  35. * of the operation on behalf of the
  36. * <code>DragSource</code> and dispatching state changes to
  37. * the <code>DragSourceListener</code>.
  38. * <P>
  39. * Note that the <code>DragSourceContext</code> itself
  40. * implements the <code>DragSourceListener</code>
  41. * interface. This is to allow the platform peer
  42. * (the <code>DragSourceContextPeer</code> instance)
  43. * created by the <code>DragSource</code> to notify
  44. * the <code>DragSourceContext</code> of
  45. * changes in state in the ongiong operation. This allows the
  46. * <code>DragSourceContext</code> to interpose
  47. * itself between the platform and the
  48. * <code>DragSourceListener</code> provided by
  49. * the initiator of the operation.
  50. *
  51. * @version 1.42, 04/30/01
  52. * @since 1.2
  53. */
  54. public class DragSourceContext implements DragSourceListener {
  55. // used by updateCurrentCursor
  56. /**
  57. * An <code>int</code> used by updateCurrentCursor()
  58. * indicating that the <code>Cursor</code> should change
  59. * to the default (no drop) <code>Cursor</code>.
  60. */
  61. protected static final int DEFAULT = 0;
  62. /**
  63. * An <code>int</code> used by updateCurrentCursor()
  64. * indicating that the <code>Cursor</code>
  65. * has entered a <code>DropTarget</code>.
  66. */
  67. protected static final int ENTER = 1;
  68. /**
  69. * An <code>int</code> used by updateCurrentCursor()
  70. * indicating that the <code>Cursor</code> is
  71. * over a <code>DropTarget</code>.
  72. */
  73. protected static final int OVER = 2;
  74. /**
  75. * An <code>int</code> used by updateCurrentCursor()
  76. * indicating that the user operation has changed.
  77. */
  78. protected static final int CHANGED = 3;
  79. /**
  80. * Called from <code>DragSource</code>, this constructor creates a new
  81. * <code>DragSourceContext</code> given the
  82. * <code>DragSourceContextPeer</code> for this Drag, the
  83. * <code>DragGestureEvent</code> that triggered the Drag, the initial
  84. * <code>Cursor</code> to use for the Drag, an (optional)
  85. * <code>Image</code> to display while the Drag is taking place, the offset
  86. * of the <code>Image</code> origin from the hotspot at the instant of the
  87. * triggering event, the <code>Transferable</code> subject data, and the
  88. * <code>DragSourceListener</code> to use during the Drag and Drop
  89. * operation.
  90. *
  91. * @param dscp the <code>DragSourceContextPeer</code> for this drag
  92. * @param trigger the triggering event
  93. * @param dragCursor the initial <code>Cursor</code>
  94. * @param dragImage the <code>Image</code> to drag (or <code>null</code>)
  95. * @param offset the offset of the image origin from the hotspot at the
  96. * instant of the triggering event
  97. * @param t the <code>Transferable</code>
  98. * @param dsl the <code>DragSourceListener</code>
  99. *
  100. * @throws IllegalArgumentException if trigger instance is incomplete
  101. * @throws NullPointerException if dscp, dsl, trigger, or t are null, or
  102. * if dragImage is non-null and offset is null
  103. */
  104. public DragSourceContext(DragSourceContextPeer dscp, DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point offset, Transferable t, DragSourceListener dsl) {
  105. if ((peer = dscp) == null)
  106. throw new NullPointerException("DragSourceContextPeer");
  107. if ((this.trigger = trigger) == null)
  108. throw new NullPointerException("Trigger");
  109. if ((dragSource = trigger.getDragSource()) == null)
  110. throw new NullPointerException("DragSource");
  111. if ((component = trigger.getComponent()) == null)
  112. throw new NullPointerException("Component");
  113. if ((actions = trigger.getSourceAsDragGestureRecognizer().getSourceActions()) == DnDConstants.ACTION_NONE)
  114. throw new IllegalArgumentException("source actions");
  115. if ((currentDropAction = trigger.getDragAction()) == DnDConstants.ACTION_NONE)
  116. throw new IllegalArgumentException("no drag action");
  117. if (t == null)
  118. throw new NullPointerException("Transferable");
  119. if (dsl == null)
  120. throw new NullPointerException("DragSourceListener");
  121. if (image != null && offset == null)
  122. throw new NullPointerException("offset");
  123. cursor = dragCursor;
  124. image = dragImage;
  125. this.offset = offset;
  126. transferable = t;
  127. listener = dsl;
  128. if (dragCursor != null) {
  129. useCustomCursor = true;
  130. }
  131. updateCurrentCursor(currentDropAction, actions, DEFAULT);
  132. }
  133. /**
  134. * This method returns the <code>DragSource</code>
  135. * that instantiated this <code>DragSourceContext</code>.
  136. * <P>
  137. * @return the <code>DragSource</code> that
  138. * instantiated this <code>DragSourceContext</code>
  139. */
  140. public DragSource getDragSource() { return dragSource; }
  141. /**
  142. * This method returns the <code>Component</code> associated with this
  143. * <code>DragSourceContext</code>.
  144. * <P>
  145. * @return the <code>Component</code> that started the drag
  146. */
  147. public Component getComponent() { return component; }
  148. /**
  149. * This method returns the <code>DragGestureEvent</code>
  150. * that initially triggered the drag.
  151. * <P>
  152. * @return the Event that triggered the drag
  153. */
  154. public DragGestureEvent getTrigger() { return trigger; }
  155. /**
  156. * This method returns an <code>int</code>
  157. * representing the current action(s)
  158. * associated with this <code>DragSourceContext.</code>
  159. * <P>
  160. * @return the current actions
  161. */
  162. public int getSourceActions() {
  163. return actions;
  164. }
  165. /**
  166. * This method sets the current drag <code>Cursor.</code>
  167. * <P>
  168. * @param c the <code>Cursor</code> to display.
  169. * Note that while <code>null</code> is not prohibited,
  170. * it is not an acceptable value for this parameter.
  171. */
  172. public void setCursor(Cursor c) {
  173. useCustomCursor = (c != null);
  174. setCursorImpl(c);
  175. }
  176. /**
  177. * This method returns the current drag <code>Cursor</code>.
  178. * <P>
  179. * @return the current drag <code>Cursor</code>
  180. */
  181. public Cursor getCursor() { return cursor; }
  182. /**
  183. * Add a <code>DragSourceListener</code> to this
  184. * <code>DragSourceContext</code> if one has not already been added.
  185. * If a <code>DragSourceListener</code> already exists,
  186. * this method throws a <code>TooManyListenersException</code>.
  187. * <P>
  188. * @param dsl the <code>DragSourceListener</code> to add.
  189. * Note that while <code>null</code> is not prohibited,
  190. * it is not acceptable as a parameter.
  191. * <P>
  192. * @throws <code>TooManyListenersException</code> if
  193. * a <code>DragSourceListener</code> has already been added
  194. */
  195. public synchronized void addDragSourceListener(DragSourceListener dsl) throws TooManyListenersException {
  196. if (dsl == null) return;
  197. if (equals(dsl)) throw new IllegalArgumentException("DragSourceContext may not be its own listener");
  198. if (listener != null)
  199. throw new TooManyListenersException();
  200. else
  201. listener = dsl;
  202. }
  203. /**
  204. * This method removes the specified <code>DragSourceListener</code>
  205. * from this <code>DragSourceContext</code>.
  206. * <P>
  207. * @param dsl the <code>DragSourceListener</code> to remove.
  208. * Note that while <code>null</code> is not prohibited,
  209. * it is not acceptable as a parameter.
  210. */
  211. public synchronized void removeDragSourceListener(DragSourceListener dsl) {
  212. if (listener != null && listener.equals(dsl)) {
  213. listener = null;
  214. } else
  215. throw new IllegalArgumentException();
  216. }
  217. /**
  218. * This method notifies the peer that
  219. * the Transferable's DataFlavors have changed.
  220. */
  221. public void transferablesFlavorsChanged() {
  222. if (peer != null) peer.transferablesFlavorsChanged();
  223. }
  224. /**
  225. * This method
  226. * intercepts the <code>DragSourceDragEvent</code>
  227. * associated with dragEnter() from the peer.
  228. * <P>
  229. * Note: This method is called by the peer implementation, not the user.
  230. * <P>
  231. * @param dsde the intercepted <code>DragSourceDragEvent</code>
  232. */
  233. public synchronized void dragEnter(DragSourceDragEvent dsde) {
  234. if (listener != null) listener.dragEnter(dsde);
  235. updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), ENTER);
  236. }
  237. /**
  238. * This method
  239. * intercepts the <code>DragSourceDragEvent</code>
  240. * associated with dragOver() from the peer.
  241. * <P>
  242. * Note: This method is called by the peer implementation, not the user.
  243. * <P>
  244. * @param dsde the intercepted <code>DragSourceDragEvent</code>
  245. */
  246. public synchronized void dragOver(DragSourceDragEvent dsde) {
  247. if (listener != null) listener.dragOver(dsde);
  248. updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), OVER);
  249. }
  250. /**
  251. * This method intercepts the <code>DragSourceEvent</code>
  252. * associated with dragExit() from the peer.
  253. * <P>
  254. * Note: This method is called by the peer implementation, not the user.
  255. * <P>
  256. * @param dse the intercepted <code>DragSourceEvent</code>
  257. */
  258. public synchronized void dragExit(DragSourceEvent dse) {
  259. if (listener != null) listener.dragExit(dse);
  260. updateCurrentCursor(currentDropAction, DnDConstants.ACTION_NONE, DEFAULT);
  261. }
  262. /**
  263. * This method intercepts the <code>DragSourceDragEvent</code>
  264. * associated with dropActionChanged() from the peer.
  265. * <P>
  266. * Note: This method is called by the peer implementation, not the user.
  267. * <P>
  268. * @param dsde the intercepted <code>DragSourceDragEvent</code>
  269. */
  270. public synchronized void dropActionChanged(DragSourceDragEvent dsde) {
  271. currentDropAction = dsde.getDropAction();
  272. if (listener != null) listener.dropActionChanged(dsde);
  273. updateCurrentCursor(currentDropAction, dsde.getTargetActions(), CHANGED);
  274. }
  275. /**
  276. * This method intercepts the <code>DragSourceDropEvent</code>
  277. * associated with dragDropEnd() from the peer.
  278. * <P>
  279. * Note: This method is called by the peer implementation, not the user.
  280. * The value of <code>null</code> is not acceptable as a parameter
  281. * to this method.
  282. * <P>
  283. * @param dsde the intercepted <code>DragSourceDropEvent</code>
  284. */
  285. public synchronized void dragDropEnd(DragSourceDropEvent dsde) {
  286. if (listener != null) listener.dragDropEnd(dsde);
  287. }
  288. /**
  289. * This method returns the <code>Transferable</code>
  290. * associated with this <code>DragSourceContext.</code>
  291. * <P>
  292. * @return the <code>Transferable</code>
  293. */
  294. public Transferable getTransferable() { return transferable; }
  295. /**
  296. * check the cursor for updates and implement defaults
  297. * <P>
  298. * @param dropOp the user's currently selected operation
  299. * @param targetAct the current target's supported actions
  300. * @param status the constant
  301. */
  302. protected void updateCurrentCursor(int dropOp, int targetAct, int status) {
  303. // if the cursor has been previously set then dont do any defaults
  304. // processing.
  305. if (useCustomCursor) {
  306. return;
  307. }
  308. // do defaults processing
  309. Cursor c = null;
  310. switch (status) {
  311. default:
  312. targetAct = DnDConstants.ACTION_NONE;
  313. case ENTER:
  314. case OVER:
  315. case CHANGED:
  316. int ra = dropOp & targetAct;
  317. if (ra == DnDConstants.ACTION_NONE) { // no drop possible
  318. if ((dropOp & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
  319. c = DragSource.DefaultLinkNoDrop;
  320. else if ((dropOp & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
  321. c = DragSource.DefaultMoveNoDrop;
  322. else
  323. c = DragSource.DefaultCopyNoDrop;
  324. } else { // drop possible
  325. if ((ra & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
  326. c = DragSource.DefaultLinkDrop;
  327. else if ((ra & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
  328. c = DragSource.DefaultMoveDrop;
  329. else
  330. c = DragSource.DefaultCopyDrop;
  331. }
  332. }
  333. setCursorImpl(c);
  334. }
  335. private void setCursorImpl(Cursor c) {
  336. if (cursor == null || !cursor.equals(c)) {
  337. cursor = c;
  338. if (peer != null) peer.setCursor(cursor);
  339. }
  340. }
  341. /*
  342. * fields
  343. */
  344. private DragSource dragSource;
  345. private DragSourceContextPeer peer;
  346. private DragGestureEvent trigger;
  347. private Cursor cursor;
  348. private Component component;
  349. private int actions;
  350. private int currentDropAction;
  351. private Image image;
  352. private Point offset;
  353. private Transferable transferable;
  354. private DragSourceListener listener;
  355. private boolean useCustomCursor = false;
  356. }