You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DragSourceExtension.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui.dnd;
  17. import java.util.Collections;
  18. import java.util.LinkedHashMap;
  19. import java.util.Map;
  20. import java.util.Objects;
  21. import com.vaadin.server.AbstractExtension;
  22. import com.vaadin.server.Resource;
  23. import com.vaadin.shared.Registration;
  24. import com.vaadin.shared.ui.dnd.criteria.ComparisonOperator;
  25. import com.vaadin.shared.ui.dnd.DragSourceRpc;
  26. import com.vaadin.shared.ui.dnd.DragSourceState;
  27. import com.vaadin.shared.ui.dnd.DropEffect;
  28. import com.vaadin.shared.ui.dnd.EffectAllowed;
  29. import com.vaadin.shared.ui.dnd.criteria.Payload;
  30. import com.vaadin.ui.AbstractComponent;
  31. import com.vaadin.ui.dnd.event.DragEndEvent;
  32. import com.vaadin.ui.dnd.event.DragEndListener;
  33. import com.vaadin.ui.dnd.event.DragStartEvent;
  34. import com.vaadin.ui.dnd.event.DragStartListener;
  35. /**
  36. * Extension to make a component drag source for HTML5 drag and drop
  37. * functionality.
  38. *
  39. * @param <T>
  40. * Type of the component to be extended.
  41. * @author Vaadin Ltd
  42. * @since 8.1
  43. */
  44. public class DragSourceExtension<T extends AbstractComponent>
  45. extends AbstractExtension {
  46. private Registration dragStartListenerHandle;
  47. private Registration dragEndListenerHandle;
  48. /**
  49. * Stores the server side drag data that is available for the drop target if
  50. * it is in the same UI.
  51. */
  52. private Object dragData;
  53. /**
  54. * Extends {@code target} component and makes it a drag source.
  55. *
  56. * @param target
  57. * Component to be extended.
  58. */
  59. public DragSourceExtension(T target) {
  60. super.extend(target);
  61. initListeners();
  62. }
  63. /**
  64. * Initializes dragstart and -end event listeners for this drag source to
  65. * capture the active drag source for the UI.
  66. */
  67. private void initListeners() {
  68. // Set current extension as active drag source in the UI
  69. dragStartListenerHandle = addDragStartListener(
  70. event -> getUI().setActiveDragSource(this));
  71. // Remove current extension as active drag source from the UI
  72. dragEndListenerHandle = addDragEndListener(
  73. event -> getUI().setActiveDragSource(null));
  74. }
  75. @Override
  76. public void attach() {
  77. super.attach();
  78. registerDragSourceRpc();
  79. }
  80. /**
  81. * Registers the server side RPC methods invoked from client side on
  82. * <code>dragstart</code> and <code>dragend</code> events.
  83. * <p>
  84. * Override this method if you have custom RPC interface for transmitting
  85. * those events with more data. If just need to do additional things before
  86. * firing the events, then you should override {@link #onDragStart()} and
  87. * {@link #onDragEnd(DropEffect)} instead.
  88. */
  89. protected void registerDragSourceRpc() {
  90. registerRpc(new DragSourceRpc() {
  91. @Override
  92. public void dragStart() {
  93. onDragStart();
  94. }
  95. @Override
  96. public void dragEnd(DropEffect dropEffect) {
  97. onDragEnd(dropEffect);
  98. }
  99. });
  100. }
  101. /**
  102. * Method invoked when a <code>dragstart</code> has been sent from client
  103. * side. Fires the {@link DragStartEvent}.
  104. */
  105. protected void onDragStart() {
  106. DragStartEvent<T> event = new DragStartEvent<>(getParent(),
  107. getState(false).effectAllowed);
  108. fireEvent(event);
  109. }
  110. /**
  111. * Method invoked when a <code>dragend</code> has been sent from client
  112. * side. Fires the {@link DragEndEvent}.
  113. *
  114. * @param dropEffect
  115. * the drop effect on the dragend
  116. */
  117. protected void onDragEnd(DropEffect dropEffect) {
  118. DragEndEvent<T> event = new DragEndEvent<>(getParent(), dropEffect);
  119. fireEvent(event);
  120. }
  121. @Override
  122. public void remove() {
  123. super.remove();
  124. // Remove listeners attached on construction
  125. dragStartListenerHandle.remove();
  126. dragEndListenerHandle.remove();
  127. }
  128. /**
  129. * Sets the allowed effects for the current drag source element. Used for
  130. * setting client side {@code DataTransfer.effectAllowed} parameter for the
  131. * drag event.
  132. * <p>
  133. * By default the value is {@link EffectAllowed#UNINITIALIZED} which is
  134. * equivalent to {@link EffectAllowed#ALL}.
  135. *
  136. * @param effect
  137. * Effects to allow for this draggable element. Cannot be {@code
  138. * null}.
  139. */
  140. public void setEffectAllowed(EffectAllowed effect) {
  141. if (effect == null) {
  142. throw new IllegalArgumentException("Allowed effect cannot be null");
  143. }
  144. if (!Objects.equals(getState(false).effectAllowed, effect)) {
  145. getState().effectAllowed = effect;
  146. }
  147. }
  148. /**
  149. * Returns the allowed effects for the current drag source element. Used to
  150. * set client side {@code DataTransfer.effectAllowed} parameter for the drag
  151. * event.
  152. * <p>
  153. * You can use different types of data to support dragging to different
  154. * targets. Accepted types depend on the drop target and those can be
  155. * platform specific. See https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types
  156. * for examples on different types.
  157. * <p>
  158. * <em>NOTE: IE11 only supports type ' text', which can be set using {@link
  159. * #setDataTransferText(String data)}</em>
  160. *
  161. * @return Effects that are allowed for this draggable element.
  162. */
  163. public EffectAllowed getEffectAllowed() {
  164. return getState(false).effectAllowed;
  165. }
  166. /**
  167. * Sets data for this drag source element with the given type. The data is
  168. * set for the client side draggable element using {@code
  169. * DataTransfer.setData(type, data)} method.
  170. * <p>
  171. * Note that {@code "text"} is the only cross browser supported data type.
  172. * Use {@link #setDataTransferText(String)} method instead if your
  173. * application supports IE11.
  174. *
  175. * @param type
  176. * Type of the data to be set for the client side draggable element,
  177. * e.g. {@code text/plain}. Cannot be {@code null}.
  178. * @param data
  179. * Data to be set for the client side draggable element. Cannot be
  180. * {@code null}.
  181. */
  182. public void setDataTransferData(String type, String data) {
  183. if (type == null) {
  184. throw new IllegalArgumentException("Data type cannot be null");
  185. }
  186. if (data == null) {
  187. throw new IllegalArgumentException("Data cannot be null");
  188. }
  189. if (!getState(false).types.contains(type)) {
  190. getState().types.add(type);
  191. }
  192. getState().data.put(type, data);
  193. }
  194. /**
  195. * Returns the data stored with type {@code type} in this drag source
  196. * element.
  197. *
  198. * @param type
  199. * Type of the requested data, e.g. {@code text/plain}.
  200. * @return Data of type {@code type} stored in this drag source element.
  201. */
  202. public String getDataTransferData(String type) {
  203. return getState(false).data.get(type);
  204. }
  205. /**
  206. * Returns the map of data stored in this drag source element. The returned
  207. * map preserves the order of storage and is unmodifiable.
  208. *
  209. * @return Unmodifiable copy of the map of data in the order the data was
  210. * stored.
  211. */
  212. public Map<String, String> getDataTransferData() {
  213. Map<String, String> data = getState(false).data;
  214. // Create a map of data that preserves the order of types
  215. LinkedHashMap<String, String> orderedData = new LinkedHashMap<>(
  216. data.size());
  217. getState(false).types
  218. .forEach(type -> orderedData.put(type, data.get(type)));
  219. return Collections.unmodifiableMap(orderedData);
  220. }
  221. /**
  222. * Sets data of type {@code "text"} for this drag source element. The data
  223. * is set for the client side draggable element using the {@code
  224. * DataTransfer.setData("text", data)} method.
  225. * <p>
  226. * Note that {@code "text"} is the only cross browser supported data type.
  227. * Use this method if your application supports IE11.
  228. *
  229. * @param data
  230. * Data to be set for the client side draggable element.
  231. * @see #setDataTransferData(String, String)
  232. */
  233. public void setDataTransferText(String data) {
  234. setDataTransferData(DragSourceState.DATA_TYPE_TEXT, data);
  235. }
  236. /**
  237. * Returns the data stored with type {@code "text"} in this drag source
  238. * element.
  239. *
  240. * @return Data of type {@code "text"} stored in this drag source element.
  241. */
  242. public String getDataTransferText() {
  243. return getDataTransferData(DragSourceState.DATA_TYPE_TEXT);
  244. }
  245. /**
  246. * Clears data with the given type for this drag source element when
  247. * present.
  248. *
  249. * @param type
  250. * Type of data to be cleared. Cannot be {@code null}.
  251. */
  252. public void clearDataTransferData(String type) {
  253. if (type == null) {
  254. throw new IllegalArgumentException("Data type cannot be null");
  255. }
  256. getState().types.remove(type);
  257. getState().data.remove(type);
  258. }
  259. /**
  260. * Clears all data for this drag source element.
  261. */
  262. public void clearDataTransferData() {
  263. getState().types.clear();
  264. getState().data.clear();
  265. }
  266. /**
  267. * Sets payload for this drag source to use with acceptance criterion. The
  268. * payload is transferred as data type in the data transfer object in the
  269. * following format: {@code "v-item:string:key:value"}. The given value is
  270. * compared to the criterion value when the drag source is dragged on top of
  271. * a drop target that has the suitable criterion.
  272. * <p>
  273. * Note that setting payload in Internet Explorer 11 is not possible due to
  274. * the browser's limitations.
  275. *
  276. * @param key
  277. * key of the payload to be transferred
  278. * @param value
  279. * value of the payload to be transferred
  280. * @see DropTargetExtension#setDropCriterion(String, String)
  281. */
  282. public void setPayload(String key, String value) {
  283. setPayload(key, String.valueOf(value), Payload.ValueType.STRING);
  284. }
  285. /**
  286. * Sets payload for this drag source to use with acceptance criterion. The
  287. * payload is transferred as data type in the data transfer object in the
  288. * following format: {@code "v-item:integer:key:value"}. The given value is
  289. * compared to the criterion value when the drag source is dragged on top of
  290. * a drop target that has the suitable criterion.
  291. * <p>
  292. * Note that setting payload in Internet Explorer 11 is not possible due to
  293. * the browser's limitations.
  294. *
  295. * @param key
  296. * key of the payload to be transferred
  297. * @param value
  298. * value of the payload to be transferred
  299. * @see DropTargetExtension#setDropCriterion(String, ComparisonOperator,
  300. * int)
  301. */
  302. public void setPayload(String key, int value) {
  303. setPayload(key, String.valueOf(value), Payload.ValueType.INTEGER);
  304. }
  305. /**
  306. * Sets payload for this drag source to use with acceptance criterion. The
  307. * payload is transferred as data type in the data transfer object in the
  308. * following format: {@code "v-item:double:key:value"}. The given value is
  309. * compared to the criterion value when the drag source is dragged on top of
  310. * a drop target that has the suitable criterion.
  311. * <p>
  312. * Note that setting payload in Internet Explorer 11 is not possible due to
  313. * the browser's limitations.
  314. *
  315. * @param key
  316. * key of the payload to be transferred
  317. * @param value
  318. * value of the payload to be transferred
  319. * @see DropTargetExtension#setDropCriterion(String, ComparisonOperator,
  320. * double)
  321. */
  322. public void setPayload(String key, double value) {
  323. setPayload(key, String.valueOf(value), Payload.ValueType.DOUBLE);
  324. }
  325. private void setPayload(String key, String value,
  326. Payload.ValueType valueType) {
  327. getState().payload.put(key, new Payload(key, value, valueType));
  328. }
  329. /**
  330. * Set server side drag data. This data is available in the drop event and
  331. * can be used to transfer data between drag source and drop target if they
  332. * are in the same UI.
  333. *
  334. * @param data
  335. * Data to transfer to drop event.
  336. */
  337. public void setDragData(Object data) {
  338. dragData = data;
  339. }
  340. /**
  341. * Get server side drag data. This data is available in the drop event and
  342. * can be used to transfer data between drag source and drop target if they
  343. * are in the same UI.
  344. *
  345. * @return Server side drag data if set, otherwise {@literal null}.
  346. */
  347. public Object getDragData() {
  348. return dragData;
  349. }
  350. /**
  351. * Attaches dragstart listener for the current drag source.
  352. * {@link DragStartListener#dragStart(DragStartEvent)} is called when
  353. * dragstart event happens on the client side.
  354. *
  355. * @param listener
  356. * Listener to handle dragstart event.
  357. * @return Handle to be used to remove this listener.
  358. */
  359. public Registration addDragStartListener(DragStartListener<T> listener) {
  360. return addListener(DragSourceState.EVENT_DRAGSTART,
  361. DragStartEvent.class, listener,
  362. DragStartListener.DRAGSTART_METHOD);
  363. }
  364. /**
  365. * Attaches dragend listener for the current drag source.
  366. * {@link DragEndListener#dragEnd(DragEndEvent)} is called when dragend
  367. * event happens on the client side.
  368. *
  369. * @param listener
  370. * Listener to handle dragend event.
  371. * @return Handle to be used to remove this listener.
  372. */
  373. public Registration addDragEndListener(DragEndListener<T> listener) {
  374. return addListener(DragSourceState.EVENT_DRAGEND, DragEndEvent.class,
  375. listener, DragEndListener.DRAGEND_METHOD);
  376. }
  377. /**
  378. * Set a custom drag image for the current drag source.
  379. *
  380. * @param imageResource
  381. * Resource of the image to be displayed as drag image.
  382. */
  383. public void setDragImage(Resource imageResource) {
  384. setResource(DragSourceState.RESOURCE_DRAG_IMAGE, imageResource);
  385. }
  386. @Override
  387. protected DragSourceState getState() {
  388. return (DragSourceState) super.getState();
  389. }
  390. @Override
  391. protected DragSourceState getState(boolean markAsDirty) {
  392. return (DragSourceState) super.getState(markAsDirty);
  393. }
  394. /**
  395. * Returns the component this extension is attached to.
  396. *
  397. * @return Extended component.
  398. */
  399. @Override
  400. @SuppressWarnings("unchecked")
  401. public T getParent() {
  402. return (T) super.getParent();
  403. }
  404. }