Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

DragSourceExtension.java 15KB

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