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.

MultiSelectionModelConnector.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  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.client.connectors;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Set;
  24. import com.google.gwt.event.shared.HandlerRegistration;
  25. import com.google.gwt.user.client.ui.CheckBox;
  26. import com.vaadin.client.ServerConnector;
  27. import com.vaadin.client.annotations.OnStateChange;
  28. import com.vaadin.client.data.DataSource;
  29. import com.vaadin.client.data.DataSource.RowHandle;
  30. import com.vaadin.client.renderers.ComplexRenderer;
  31. import com.vaadin.client.renderers.Renderer;
  32. import com.vaadin.client.widget.grid.DataAvailableEvent;
  33. import com.vaadin.client.widget.grid.DataAvailableHandler;
  34. import com.vaadin.client.widget.grid.events.SelectAllEvent;
  35. import com.vaadin.client.widget.grid.events.SelectAllHandler;
  36. import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer;
  37. import com.vaadin.client.widget.grid.selection.SelectionModel;
  38. import com.vaadin.client.widget.grid.selection.SelectionModel.Multi;
  39. import com.vaadin.client.widget.grid.selection.SpaceSelectHandler;
  40. import com.vaadin.client.widgets.Grid;
  41. import com.vaadin.client.widgets.Grid.HeaderCell;
  42. import com.vaadin.shared.ui.Connect;
  43. import com.vaadin.shared.ui.grid.GridState;
  44. import com.vaadin.shared.ui.grid.Range;
  45. import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc;
  46. import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState;
  47. import com.vaadin.ui.LegacyGrid.MultiSelectionModel;
  48. import elemental.json.JsonObject;
  49. /**
  50. * Connector for server-side {@link MultiSelectionModel}.
  51. *
  52. * @since 7.6
  53. * @author Vaadin Ltd
  54. */
  55. @Connect(MultiSelectionModel.class)
  56. public class MultiSelectionModelConnector extends
  57. AbstractSelectionModelConnector<SelectionModel.Multi<JsonObject>> {
  58. private Multi<JsonObject> selectionModel = createSelectionModel();
  59. private SpaceSelectHandler<JsonObject> spaceHandler;
  60. @Override
  61. protected void extend(ServerConnector target) {
  62. getGrid().setSelectionModel(selectionModel);
  63. spaceHandler = new SpaceSelectHandler<JsonObject>(getGrid());
  64. }
  65. @Override
  66. public void onUnregister() {
  67. spaceHandler.removeHandler();
  68. }
  69. @Override
  70. protected Multi<JsonObject> createSelectionModel() {
  71. return new MultiSelectionModel();
  72. }
  73. @Override
  74. public MultiSelectionModelState getState() {
  75. return (MultiSelectionModelState) super.getState();
  76. }
  77. @OnStateChange("allSelected")
  78. void updateSelectAllCheckbox() {
  79. if (selectionModel.getSelectionColumnRenderer() != null) {
  80. HeaderCell cell = getGrid().getDefaultHeaderRow()
  81. .getCell(getGrid().getColumn(0));
  82. CheckBox widget = (CheckBox) cell.getWidget();
  83. widget.setValue(getState().allSelected, false);
  84. }
  85. }
  86. protected class MultiSelectionModel extends AbstractSelectionModel
  87. implements SelectionModel.Multi.Batched<JsonObject> {
  88. private ComplexRenderer<Boolean> renderer = null;
  89. private Set<RowHandle<JsonObject>> selected = new HashSet<RowHandle<JsonObject>>();
  90. private Set<RowHandle<JsonObject>> deselected = new HashSet<RowHandle<JsonObject>>();
  91. private HandlerRegistration selectAll;
  92. private HandlerRegistration dataAvailable;
  93. private Range availableRows;
  94. private boolean batchSelect = false;
  95. @Override
  96. public void setGrid(Grid<JsonObject> grid) {
  97. super.setGrid(grid);
  98. if (grid != null) {
  99. renderer = createSelectionColumnRenderer(grid);
  100. selectAll = getGrid().addSelectAllHandler(
  101. new SelectAllHandler<JsonObject>() {
  102. @Override
  103. public void onSelectAll(
  104. SelectAllEvent<JsonObject> event) {
  105. selectAll();
  106. }
  107. });
  108. dataAvailable = getGrid()
  109. .addDataAvailableHandler(new DataAvailableHandler() {
  110. @Override
  111. public void onDataAvailable(
  112. DataAvailableEvent event) {
  113. availableRows = event.getAvailableRows();
  114. }
  115. });
  116. } else if (renderer != null) {
  117. selectAll.removeHandler();
  118. dataAvailable.removeHandler();
  119. renderer = null;
  120. }
  121. }
  122. /**
  123. * Creates a selection column renderer. This method can be overridden to
  124. * use a custom renderer or use {@code null} to disable the selection
  125. * column.
  126. *
  127. * @param grid
  128. * the grid for this selection model
  129. * @return selection column renderer or {@code null} if not needed
  130. */
  131. protected ComplexRenderer<Boolean> createSelectionColumnRenderer(
  132. Grid<JsonObject> grid) {
  133. return new MultiSelectionRenderer<JsonObject>(grid);
  134. }
  135. /**
  136. * Selects all available rows, sends request to server to select
  137. * everything.
  138. */
  139. public void selectAll() {
  140. assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
  141. DataSource<JsonObject> dataSource = getGrid().getDataSource();
  142. for (int i = availableRows.getStart(); i < availableRows
  143. .getEnd(); ++i) {
  144. final JsonObject row = dataSource.getRow(i);
  145. if (row != null) {
  146. RowHandle<JsonObject> handle = dataSource.getHandle(row);
  147. markAsSelected(handle, true);
  148. }
  149. }
  150. getRpcProxy(MultiSelectionModelServerRpc.class).selectAll();
  151. }
  152. @Override
  153. public Renderer<Boolean> getSelectionColumnRenderer() {
  154. return renderer;
  155. }
  156. /**
  157. * {@inheritDoc}
  158. *
  159. * @return {@code false} if rows is empty, else {@code true}
  160. */
  161. @Override
  162. public boolean select(JsonObject... rows) {
  163. return select(Arrays.asList(rows));
  164. }
  165. /**
  166. * {@inheritDoc}
  167. *
  168. * @return {@code false} if rows is empty, else {@code true}
  169. */
  170. @Override
  171. public boolean deselect(JsonObject... rows) {
  172. return deselect(Arrays.asList(rows));
  173. }
  174. /**
  175. * {@inheritDoc}
  176. *
  177. * @return always {@code true}
  178. */
  179. @Override
  180. public boolean deselectAll() {
  181. assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
  182. DataSource<JsonObject> dataSource = getGrid().getDataSource();
  183. for (int i = availableRows.getStart(); i < availableRows
  184. .getEnd(); ++i) {
  185. final JsonObject row = dataSource.getRow(i);
  186. if (row != null) {
  187. RowHandle<JsonObject> handle = dataSource.getHandle(row);
  188. markAsSelected(handle, false);
  189. }
  190. }
  191. getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll();
  192. return true;
  193. }
  194. /**
  195. * {@inheritDoc}
  196. *
  197. * @return {@code false} if rows is empty, else {@code true}
  198. */
  199. @Override
  200. public boolean select(Collection<JsonObject> rows) {
  201. if (rows.isEmpty()) {
  202. return false;
  203. }
  204. for (JsonObject row : rows) {
  205. RowHandle<JsonObject> rowHandle = getRowHandle(row);
  206. if (markAsSelected(rowHandle, true)) {
  207. selected.add(rowHandle);
  208. }
  209. }
  210. if (!isBeingBatchSelected()) {
  211. sendSelected();
  212. }
  213. return true;
  214. }
  215. /**
  216. * Marks the given row to be selected or deselected. Returns true if the
  217. * value actually changed.
  218. * <p>
  219. * Note: If selection model is in batch select state, the row will be
  220. * pinned on select.
  221. *
  222. * @param row
  223. * row handle
  224. * @param selected
  225. * {@code true} if row should be selected; {@code false} if
  226. * not
  227. * @return {@code true} if selected status changed; {@code false} if not
  228. */
  229. protected boolean markAsSelected(RowHandle<JsonObject> row,
  230. boolean selected) {
  231. if (selected && !isSelected(row.getRow())) {
  232. row.getRow().put(GridState.JSONKEY_SELECTED, true);
  233. } else if (!selected && isSelected(row.getRow())) {
  234. row.getRow().remove(GridState.JSONKEY_SELECTED);
  235. } else {
  236. return false;
  237. }
  238. row.updateRow();
  239. if (isBeingBatchSelected()) {
  240. row.pin();
  241. }
  242. return true;
  243. }
  244. /**
  245. * {@inheritDoc}
  246. *
  247. * @return {@code false} if rows is empty, else {@code true}
  248. */
  249. @Override
  250. public boolean deselect(Collection<JsonObject> rows) {
  251. if (rows.isEmpty()) {
  252. return false;
  253. }
  254. for (JsonObject row : rows) {
  255. RowHandle<JsonObject> rowHandle = getRowHandle(row);
  256. if (markAsSelected(rowHandle, false)) {
  257. deselected.add(rowHandle);
  258. }
  259. }
  260. if (!isBeingBatchSelected()) {
  261. sendDeselected();
  262. }
  263. return true;
  264. }
  265. /**
  266. * Sends a deselect RPC call to server-side containing all deselected
  267. * rows. Unpins any pinned rows.
  268. */
  269. private void sendDeselected() {
  270. getRpcProxy(MultiSelectionModelServerRpc.class)
  271. .deselect(getRowKeys(deselected));
  272. if (isBeingBatchSelected()) {
  273. for (RowHandle<JsonObject> row : deselected) {
  274. row.unpin();
  275. }
  276. }
  277. deselected.clear();
  278. }
  279. /**
  280. * Sends a select RPC call to server-side containing all selected rows.
  281. * Unpins any pinned rows.
  282. */
  283. private void sendSelected() {
  284. getRpcProxy(MultiSelectionModelServerRpc.class)
  285. .select(getRowKeys(selected));
  286. if (isBeingBatchSelected()) {
  287. for (RowHandle<JsonObject> row : selected) {
  288. row.unpin();
  289. }
  290. }
  291. selected.clear();
  292. }
  293. private List<String> getRowKeys(Set<RowHandle<JsonObject>> handles) {
  294. List<String> keys = new ArrayList<String>();
  295. for (RowHandle<JsonObject> handle : handles) {
  296. keys.add(getRowKey(handle.getRow()));
  297. }
  298. return keys;
  299. }
  300. private Set<JsonObject> getRows(Set<RowHandle<JsonObject>> handles) {
  301. Set<JsonObject> rows = new HashSet<JsonObject>();
  302. for (RowHandle<JsonObject> handle : handles) {
  303. rows.add(handle.getRow());
  304. }
  305. return rows;
  306. }
  307. @Override
  308. public void startBatchSelect() {
  309. assert selected.isEmpty()
  310. && deselected.isEmpty() : "Row caches were not clear.";
  311. batchSelect = true;
  312. }
  313. @Override
  314. public void commitBatchSelect() {
  315. assert batchSelect : "Not batch selecting.";
  316. if (!selected.isEmpty()) {
  317. sendSelected();
  318. }
  319. if (!deselected.isEmpty()) {
  320. sendDeselected();
  321. }
  322. batchSelect = false;
  323. }
  324. @Override
  325. public boolean isBeingBatchSelected() {
  326. return batchSelect;
  327. }
  328. @Override
  329. public Collection<JsonObject> getSelectedRowsBatch() {
  330. return Collections.unmodifiableSet(getRows(selected));
  331. }
  332. @Override
  333. public Collection<JsonObject> getDeselectedRowsBatch() {
  334. return Collections.unmodifiableSet(getRows(deselected));
  335. }
  336. }
  337. }