您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

GridDropTarget.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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.components.grid;
  17. import java.util.LinkedHashMap;
  18. import java.util.Locale;
  19. import java.util.Map;
  20. import com.vaadin.shared.Registration;
  21. import com.vaadin.shared.ui.dnd.DropEffect;
  22. import com.vaadin.shared.ui.grid.DropMode;
  23. import com.vaadin.shared.ui.grid.GridDropTargetRpc;
  24. import com.vaadin.shared.ui.grid.GridDropTargetState;
  25. import com.vaadin.ui.Grid;
  26. import com.vaadin.ui.dnd.DropTargetExtension;
  27. /**
  28. * Makes the rows of a Grid HTML5 drop targets. This is the server side
  29. * counterpart of GridDropTargetExtensionConnector.
  30. *
  31. * @param <T>
  32. * Type of the Grid bean.
  33. * @author Vaadin Ltd
  34. * @since 8.1
  35. * @see GridRowDragger
  36. */
  37. public class GridDropTarget<T> extends DropTargetExtension<Grid<T>> {
  38. private Registration sortListenerRegistration;
  39. private DropMode cachedDropMode;
  40. private boolean dropAllowedOnRowsWhenSorted = true;
  41. /**
  42. * Extends a Grid and makes it's rows drop targets for HTML5 drag and drop.
  43. *
  44. * @param target
  45. * Grid to be extended.
  46. * @param dropMode
  47. * Drop mode that describes the allowed drop locations within the
  48. * Grid's row.
  49. * @see GridDropEvent#getDropLocation()
  50. */
  51. public GridDropTarget(Grid<T> target, DropMode dropMode) {
  52. super(target);
  53. setDropMode(dropMode);
  54. }
  55. /**
  56. * Gets the grid this extension has been attached to.
  57. *
  58. * @return the grid for this extension
  59. * @since 8.2
  60. */
  61. public Grid<T> getGrid() {
  62. return getParent();
  63. }
  64. /**
  65. * Sets the drop mode of this drop target.
  66. * <p>
  67. * When using {@link DropMode#ON_TOP}, and the grid is either empty or has
  68. * empty space after the last row, the drop can still happen on the empty
  69. * space, and the {@link GridDropEvent#getDropTargetRow()} will return an
  70. * empty optional.
  71. * <p>
  72. * When using {@link DropMode#BETWEEN} or
  73. * {@link DropMode#ON_TOP_OR_BETWEEN}, and there is at least one row in the
  74. * grid, any drop after the last row in the grid will get the last row as
  75. * the {@link GridDropEvent#getDropTargetRow()}. If there are no rows in the
  76. * grid, then it will return an empty optional.
  77. * <p>
  78. * If using {@link DropMode#ON_GRID}, then the drop will not happen on any
  79. * row, but instead just "on the grid". The target row will not be present
  80. * in this case.
  81. * <p>
  82. * <em>NOTE: {@link DropMode#ON_GRID} is used automatically when the grid
  83. * has been sorted and {@link #setDropAllowedOnRowsWhenSorted(boolean)} is
  84. * {@code false} - since the drop location would not necessarily match the
  85. * correct row because of the sorting. During the sorting, any calls to this
  86. * method don't have any effect until the sorting has been removed, or
  87. * {@link #setDropAllowedOnRowsWhenSorted(boolean)} is set back to
  88. * {@code true}.</em>
  89. *
  90. * @param dropMode
  91. * Drop mode that describes the allowed drop locations within the
  92. * Grid's row.
  93. * @see GridDropEvent#getDropLocation()
  94. * @see #setDropAllowedOnRowsWhenSorted(boolean)
  95. */
  96. public void setDropMode(DropMode dropMode) {
  97. if (dropMode == null) {
  98. throw new IllegalArgumentException("Drop mode cannot be null");
  99. }
  100. if (cachedDropMode != null) {
  101. cachedDropMode = dropMode;
  102. } else {
  103. internalSetDropMode(dropMode);
  104. }
  105. }
  106. private void internalSetDropMode(DropMode dropMode) {
  107. getState().dropMode = dropMode;
  108. }
  109. /**
  110. * Gets the drop mode of this drop target.
  111. *
  112. * @return Drop mode that describes the allowed drop locations within the
  113. * Grid's row.
  114. */
  115. public DropMode getDropMode() {
  116. return getState(false).dropMode;
  117. }
  118. /**
  119. * Sets whether the grid accepts drop on rows as target when the grid has
  120. * been sorted by the user.
  121. * <p>
  122. * Default value is {@code true} for backwards compatibility with 8.1. When
  123. * {@code true} is used or the grid is not sorted, the mode used in
  124. * {@link #setDropMode(DropMode)} is always used.
  125. * <p>
  126. * {@code false} value means that when the grid has been sorted, the drop
  127. * mode is always {@link DropMode#ON_GRID}, regardless of what was set with
  128. * {@link #setDropMode(DropMode)}. Once the grid is not sorted anymore, the
  129. * sort mode is reverted back to what was set with
  130. * {@link #setDropMode(DropMode)}.
  131. *
  132. * @param dropAllowedOnSortedGridRows
  133. * {@code true} for allowing, {@code false} for not allowing
  134. * drops on sorted grid rows
  135. * @since 8.2
  136. */
  137. public void setDropAllowedOnRowsWhenSorted(
  138. boolean dropAllowedOnSortedGridRows) {
  139. if (this.dropAllowedOnRowsWhenSorted != dropAllowedOnSortedGridRows) {
  140. this.dropAllowedOnRowsWhenSorted = dropAllowedOnSortedGridRows;
  141. if (!dropAllowedOnSortedGridRows) {
  142. sortListenerRegistration = getParent()
  143. .addSortListener(event -> {
  144. updateDropModeForSortedGrid(
  145. !event.getSortOrder().isEmpty());
  146. });
  147. updateDropModeForSortedGrid(
  148. !getParent().getSortOrder().isEmpty());
  149. } else {
  150. // if the grid has been sorted, but now dropping on sorted grid
  151. // is allowed, switch back to the previously allowed drop mode
  152. if (cachedDropMode != null) {
  153. internalSetDropMode(cachedDropMode);
  154. }
  155. sortListenerRegistration.remove();
  156. sortListenerRegistration = null;
  157. cachedDropMode = null;
  158. }
  159. }
  160. }
  161. private void updateDropModeForSortedGrid(boolean sorted) {
  162. if (sorted && cachedDropMode == null) {
  163. cachedDropMode = getDropMode();
  164. internalSetDropMode(DropMode.ON_GRID);
  165. } else if (!sorted && cachedDropMode != null) {
  166. internalSetDropMode(cachedDropMode);
  167. cachedDropMode = null;
  168. }
  169. }
  170. /**
  171. * Gets whether drops are allowed on rows as target, when the user has
  172. * sorted the grid.
  173. *
  174. * @return whether drop are allowed for the grid's rows when user has sorted
  175. * the grid
  176. * @since 8.2
  177. */
  178. public boolean isDropAllowedOnRowsWhenSorted() {
  179. return dropAllowedOnRowsWhenSorted;
  180. }
  181. /**
  182. * Attaches drop listener for the current drop target.
  183. * {@link GridDropListener#drop(GridDropEvent)} is called when drop event
  184. * happens on the client side.
  185. *
  186. * @param listener
  187. * Listener to handle drop event.
  188. * @return Handle to be used to remove this listener.
  189. */
  190. public Registration addGridDropListener(GridDropListener<T> listener) {
  191. return addListener(GridDropEvent.class, listener,
  192. GridDropListener.DROP_METHOD);
  193. }
  194. /**
  195. * Sets the threshold between drop locations from the top and the bottom of
  196. * a row in pixels.
  197. * <p>
  198. * Dropping an element
  199. * <ul>
  200. * <li>within {@code threshold} pixels from the top of a row results in a
  201. * drop event with {@link com.vaadin.shared.ui.grid.DropLocation#ABOVE
  202. * DropLocation.ABOVE}</li>
  203. * <li>within {@code threshold} pixels from the bottom of a row results in a
  204. * drop event with {@link com.vaadin.shared.ui.grid.DropLocation#BELOW
  205. * DropLocation.BELOW}</li>
  206. * <li>anywhere else within the row results in a drop event with
  207. * {@link com.vaadin.shared.ui.grid.DropLocation#ON_TOP
  208. * DropLocation.ON_TOP}</li>
  209. * </ul>
  210. * The value only has an effect when drop mode is set to
  211. * {@link DropMode#ON_TOP_OR_BETWEEN}.
  212. * <p>
  213. * Default is 5 pixels.
  214. *
  215. * @param threshold
  216. * The threshold from the top and bottom of the row in pixels.
  217. */
  218. public void setDropThreshold(int threshold) {
  219. getState().dropThreshold = threshold;
  220. }
  221. /**
  222. * Gets the threshold between drop locations from the top and the bottom of
  223. * the row.
  224. *
  225. * @return The threshold in pixels.
  226. */
  227. public int getDropThreshold() {
  228. return getState(false).dropThreshold;
  229. }
  230. @Override
  231. protected void registerDropTargetRpc() {
  232. registerRpc((GridDropTargetRpc) (types, data, dropEffect, rowKey,
  233. dropLocation, mouseEventDetails) -> {
  234. // Create a linked map that preserves the order of types
  235. Map<String, String> dataPreserveOrder = new LinkedHashMap<>();
  236. types.forEach(type -> dataPreserveOrder.put(type, data.get(type)));
  237. T dropTargetRow = getParent().getDataCommunicator().getKeyMapper()
  238. .get(rowKey);
  239. GridDropEvent<T> event = new GridDropEvent<>(getParent(),
  240. dataPreserveOrder,
  241. DropEffect.valueOf(dropEffect.toUpperCase(Locale.ROOT)),
  242. getUI().getActiveDragSource(), dropTargetRow, dropLocation,
  243. mouseEventDetails);
  244. fireEvent(event);
  245. });
  246. }
  247. @Override
  248. protected GridDropTargetState getState() {
  249. return (GridDropTargetState) super.getState();
  250. }
  251. @Override
  252. protected GridDropTargetState getState(boolean markAsDirty) {
  253. return (GridDropTargetState) super.getState(markAsDirty);
  254. }
  255. @Override
  256. public void remove() {
  257. super.remove();
  258. // this handler can be removed from the grid and cannot be added to
  259. // another grid, thus enough to just remove the listener
  260. if (sortListenerRegistration != null) {
  261. sortListenerRegistration.remove();
  262. sortListenerRegistration = null;
  263. }
  264. }
  265. }