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