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.

DropTargetExtension.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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.Arrays;
  18. import java.util.LinkedHashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Objects;
  22. import com.vaadin.server.AbstractExtension;
  23. import com.vaadin.shared.MouseEventDetails;
  24. import com.vaadin.shared.Registration;
  25. import com.vaadin.shared.ui.dnd.criteria.ComparisonOperator;
  26. import com.vaadin.shared.ui.dnd.DropEffect;
  27. import com.vaadin.shared.ui.dnd.DropTargetRpc;
  28. import com.vaadin.shared.ui.dnd.DropTargetState;
  29. import com.vaadin.shared.ui.dnd.criteria.Criterion;
  30. import com.vaadin.ui.AbstractComponent;
  31. import com.vaadin.ui.dnd.event.DropEvent;
  32. import com.vaadin.ui.dnd.event.DropListener;
  33. /**
  34. * Extension to make a component a drop target for HTML5 drag and drop
  35. * functionality.
  36. *
  37. * @param <T>
  38. * Type of the component to be extended.
  39. * @author Vaadin Ltd
  40. * @since 8.1
  41. */
  42. public class DropTargetExtension<T extends AbstractComponent>
  43. extends AbstractExtension {
  44. /**
  45. * Extends {@code target} component and makes it a drop target.
  46. *
  47. * @param target
  48. * Component to be extended.
  49. */
  50. public DropTargetExtension(T target) {
  51. super.extend(target);
  52. }
  53. @Override
  54. public void attach() {
  55. super.attach();
  56. registerDropTargetRpc();
  57. }
  58. /**
  59. * Registers the server side RPC methods invoked from client side on
  60. * <code>drop</code> event.
  61. * <p>
  62. * Override this method if you need to have a custom RPC interface for
  63. * transmitting the drop event with more data. If just need to do additional
  64. * things before firing the drop event, then you should override
  65. * {@link #onDrop(List, Map, DropEffect, MouseEventDetails)} instead.
  66. */
  67. protected void registerDropTargetRpc() {
  68. registerRpc(
  69. (DropTargetRpc) (types, data, dropEffect, mouseEventDetails) -> {
  70. onDrop(types, data,
  71. DropEffect.valueOf(dropEffect.toUpperCase()),
  72. mouseEventDetails);
  73. });
  74. }
  75. /**
  76. * Invoked when a <code>drop</code> has been received from client side.
  77. * Fires the {@link DropEvent}.
  78. *
  79. * @param types
  80. * List of data types from {@code DataTransfer.types} object.
  81. * @param data
  82. * Map containing all types and corresponding data from the {@code
  83. * DataTransfer} object.
  84. * @param dropEffect
  85. * the drop effect
  86. * @param mouseEventDetails
  87. * mouse event details object containing information about the drop
  88. * event
  89. */
  90. protected void onDrop(List<String> types, Map<String, String> data,
  91. DropEffect dropEffect, MouseEventDetails mouseEventDetails) {
  92. // Create a linked map that preserves the order of types
  93. Map<String, String> dataPreserveOrder = new LinkedHashMap<>();
  94. types.forEach(type -> dataPreserveOrder.put(type, data.get(type)));
  95. DropEvent<T> event = new DropEvent<>(getParent(), dataPreserveOrder,
  96. dropEffect, getUI().getActiveDragSource(), mouseEventDetails);
  97. fireEvent(event);
  98. }
  99. /**
  100. * Sets the drop effect for the current drop target. This is set to the
  101. * dropEffect on {@code dragenter} and {@code dragover} events.
  102. * <p>
  103. * <em>NOTE: If the drop effect that doesn't match the dropEffect /
  104. * effectAllowed of the drag source, it DOES NOT prevent drop on IE and
  105. * Safari! For FireFox and Chrome the drop is prevented if there they don't
  106. * match.</em>
  107. * <p>
  108. * Default value is browser dependent and can depend on e.g. modifier keys.
  109. * <p>
  110. * From Moz Foundation: "You can modify the dropEffect property during the
  111. * dragenter or dragover events, if for example, a particular drop target
  112. * only supports certain operations. You can modify the dropEffect property
  113. * to override the user effect, and enforce a specific drop operation to
  114. * occur. Note that this effect must be one listed within the effectAllowed
  115. * property. Otherwise, it will be set to an alternate value that is
  116. * allowed."
  117. *
  118. * @param dropEffect
  119. * the drop effect to be set or {@code null} to not modify
  120. */
  121. public void setDropEffect(DropEffect dropEffect) {
  122. if (!Objects.equals(getState(false).dropEffect, dropEffect)) {
  123. getState().dropEffect = dropEffect;
  124. }
  125. }
  126. /**
  127. * Returns the drop effect for the current drop target.
  128. *
  129. * @return The drop effect of this drop target or {@code null} if none set
  130. * @see #setDropEffect(DropEffect)
  131. */
  132. public DropEffect getDropEffect() {
  133. return getState(false).dropEffect;
  134. }
  135. /**
  136. * Sets a criteria script in JavaScript to allow drop on this drop target.
  137. * The script is executed when something is dragged on top of the target,
  138. * and the drop is not allowed in case the script returns {@code false}.
  139. * <p>
  140. * Drop will be allowed if it passes both this criteria script and the
  141. * criteria set via any of {@code setDropCriterion()} or {@code
  142. * setDropCriteria()} methods. If no criteria is set, then the drop is
  143. * always accepted, if the set {@link #setDropEffect(DropEffect) dropEffect}
  144. * matches the drag source.
  145. * <p>
  146. * <b>IMPORTANT:</b> Construct the criteria script carefully and do not
  147. * include untrusted sources such as user input. Always keep in mind that
  148. * the script is executed on the client as is.
  149. * <p>
  150. * Example:
  151. *
  152. * <pre>
  153. * target.setDropCriterion(
  154. * // If dragged source contains a URL, allow it to be dropped
  155. * "if (event.dataTransfer.types.includes('text/uri-list')) {" +
  156. * " return true;" +
  157. * "}" +
  158. *
  159. * // Otherwise cancel the event
  160. * "return false;");
  161. * </pre>
  162. *
  163. * @param criteriaScript
  164. * JavaScript to be executed when drop event happens or
  165. * {@code null} to clear.
  166. */
  167. public void setDropCriteriaScript(String criteriaScript) {
  168. if (!Objects.equals(getState(false).criteriaScript, criteriaScript)) {
  169. getState().criteriaScript = criteriaScript;
  170. }
  171. }
  172. /**
  173. * Gets the criteria script that determines whether a drop is allowed. If
  174. * the script returns {@code false}, then it is determined the drop is not
  175. * allowed.
  176. *
  177. * @return JavaScript that executes when drop event happens.
  178. * @see #setDropCriteriaScript(String)
  179. */
  180. public String getDropCriteriaScript() {
  181. return getState(false).criteriaScript;
  182. }
  183. /**
  184. * Set a drop criterion to allow drop on this drop target. When data is
  185. * dragged on top of the drop target, the given value is compared to the
  186. * drag source's payload with the same key. The drag passes this criterion
  187. * if the value of the payload and the value given here are equal.
  188. * <p>
  189. * Note that calling this method will overwrite the previously set criteria.
  190. * To set multiple criteria, call the {@link #setDropCriteria(Criterion.Match,
  191. * Criterion...)} method.
  192. * <p>
  193. * To handle more complex criteria, define a custom script with {@link
  194. * #setDropCriteriaScript(String)}. Drop will be allowed if both this
  195. * criterion and the criteria script are passed.
  196. *
  197. * @param key
  198. * key of the payload to be compared
  199. * @param value
  200. * value to be compared to the payload's value
  201. * @see DragSourceExtension#setPayload(String, String)
  202. */
  203. public void setDropCriterion(String key, String value) {
  204. setDropCriteria(Criterion.Match.ANY, new Criterion(key, value));
  205. }
  206. /**
  207. * Set a drop criterion to allow drop on this drop target. When data is
  208. * dragged on top of the drop target, the given value is compared to the
  209. * drag source's payload with the same key. The drag passes this criterion
  210. * if the value of the payload compared to the given value using the given
  211. * operator holds.
  212. * <p>
  213. * Note that calling this method will overwrite the previously set criteria.
  214. * To set multiple criteria, call the {@link #setDropCriteria(Criterion.Match,
  215. * Criterion...)} method.
  216. * <p>
  217. * To handle more complex criteria, define a custom script with {@link
  218. * #setDropCriteriaScript(String)}. Drop will be allowed if both this
  219. * criterion and the criteria script are passed.
  220. *
  221. * @param key
  222. * key of the payload to be compared
  223. * @param operator
  224. * comparison operator to be used
  225. * @param value
  226. * value to be compared to the payload's value
  227. * @see DragSourceExtension#setPayload(String, int)
  228. */
  229. public void setDropCriterion(String key, ComparisonOperator operator,
  230. int value) {
  231. setDropCriteria(Criterion.Match.ANY,
  232. new Criterion(key, operator, value));
  233. }
  234. /**
  235. * Set a drop criterion to allow drop on this drop target. When data is
  236. * dragged on top of the drop target, the given value is compared to the
  237. * drag source's payload with the same key. The drag passes this criterion
  238. * if the value of the payload compared to the given value using the given
  239. * operator holds.
  240. * <p>
  241. * Note that calling this method will overwrite the previously set criteria.
  242. * To set multiple criteria, call the {@link #setDropCriteria(Criterion.Match,
  243. * Criterion...)} method.
  244. * <p>
  245. * To handle more complex criteria, define a custom script with {@link
  246. * #setDropCriteriaScript(String)}. Drop will be allowed if both this
  247. * criterion and the criteria script are passed.
  248. *
  249. * @param key
  250. * key of the payload to be compared
  251. * @param operator
  252. * comparison operator to be used
  253. * @param value
  254. * value to be compared to the payload's value
  255. * @see DragSourceExtension#setPayload(String, double)
  256. */
  257. public void setDropCriterion(String key, ComparisonOperator operator,
  258. double value) {
  259. setDropCriteria(Criterion.Match.ANY,
  260. new Criterion(key, operator, value));
  261. }
  262. /**
  263. * Sets multiple drop criteria to allow drop on this drop target. When data
  264. * is dragged on top of the drop target, the value of the given criteria is
  265. * compared to the drag source's payload with the same key.
  266. * <p>
  267. * The drag passes these criteria if, depending on {@code match}, any or all
  268. * of the criteria matches the payload, that is the value of the payload
  269. * compared to the value of the criterion using the criterion's operator
  270. * holds.
  271. * <p>
  272. * Note that calling this method will overwrite the previously set
  273. * criteria.
  274. * <p>
  275. * To handle more complex criteria, define a custom script with {@link
  276. * #setDropCriteriaScript(String)}. Drop will be allowed if both this
  277. * criterion and the criteria script are passed.
  278. *
  279. * @param match
  280. * defines whether any or all of the given criteria should match to
  281. * allow drop on this drop target
  282. * @param criteria
  283. * criteria to be compared to the payload
  284. */
  285. public void setDropCriteria(Criterion.Match match, Criterion... criteria) {
  286. getState().criteriaMatch = match;
  287. getState().criteria = Arrays.asList(criteria);
  288. }
  289. /**
  290. * Attaches drop listener for the current drop target.
  291. * {@link DropListener#drop(DropEvent)} is called when drop event happens on
  292. * the client side.
  293. *
  294. * @param listener
  295. * Listener to handle drop event.
  296. * @return Handle to be used to remove this listener.
  297. */
  298. public Registration addDropListener(DropListener<T> listener) {
  299. return addListener(DropEvent.class, listener, DropListener.DROP_METHOD);
  300. }
  301. @Override
  302. protected DropTargetState getState() {
  303. return (DropTargetState) super.getState();
  304. }
  305. @Override
  306. protected DropTargetState getState(boolean markAsDirty) {
  307. return (DropTargetState) super.getState(markAsDirty);
  308. }
  309. /**
  310. * Returns the component this extension is attached to.
  311. *
  312. * @return Extended component.
  313. */
  314. @Override
  315. @SuppressWarnings("unchecked")
  316. public T getParent() {
  317. return (T) super.getParent();
  318. }
  319. }