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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /*
  2. * Copyright 2000-2014 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.Grid.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().getCell(
  81. 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().addDataAvailableHandler(
  109. new DataAvailableHandler() {
  110. @Override
  111. public void onDataAvailable(DataAvailableEvent event) {
  112. availableRows = event.getAvailableRows();
  113. }
  114. });
  115. } else if (renderer != null) {
  116. renderer.destroy();
  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.getEnd(); ++i) {
  143. final JsonObject row = dataSource.getRow(i);
  144. if (row != null) {
  145. RowHandle<JsonObject> handle = dataSource.getHandle(row);
  146. markAsSelected(handle, true);
  147. }
  148. }
  149. getRpcProxy(MultiSelectionModelServerRpc.class).selectAll();
  150. }
  151. @Override
  152. public Renderer<Boolean> getSelectionColumnRenderer() {
  153. return renderer;
  154. }
  155. /**
  156. * {@inheritDoc}
  157. *
  158. * @return {@code false} if rows is empty, else {@code true}
  159. */
  160. @Override
  161. public boolean select(JsonObject... rows) {
  162. return select(Arrays.asList(rows));
  163. }
  164. /**
  165. * {@inheritDoc}
  166. *
  167. * @return {@code false} if rows is empty, else {@code true}
  168. */
  169. @Override
  170. public boolean deselect(JsonObject... rows) {
  171. return deselect(Arrays.asList(rows));
  172. }
  173. /**
  174. * {@inheritDoc}
  175. *
  176. * @return always {@code true}
  177. */
  178. @Override
  179. public boolean deselectAll() {
  180. assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
  181. DataSource<JsonObject> dataSource = getGrid().getDataSource();
  182. for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) {
  183. final JsonObject row = dataSource.getRow(i);
  184. if (row != null) {
  185. RowHandle<JsonObject> handle = dataSource.getHandle(row);
  186. markAsSelected(handle, false);
  187. }
  188. }
  189. getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll();
  190. return true;
  191. }
  192. /**
  193. * {@inheritDoc}
  194. *
  195. * @return {@code false} if rows is empty, else {@code true}
  196. */
  197. @Override
  198. public boolean select(Collection<JsonObject> rows) {
  199. if (rows.isEmpty()) {
  200. return false;
  201. }
  202. for (JsonObject row : rows) {
  203. RowHandle<JsonObject> rowHandle = getRowHandle(row);
  204. if (markAsSelected(rowHandle, true)) {
  205. selected.add(rowHandle);
  206. }
  207. }
  208. if (!isBeingBatchSelected()) {
  209. sendSelected();
  210. }
  211. return true;
  212. }
  213. /**
  214. * Marks the given row to be selected or deselected. Returns true if the
  215. * value actually changed.
  216. * <p>
  217. * Note: If selection model is in batch select state, the row will be
  218. * pinned on select.
  219. *
  220. * @param row
  221. * row handle
  222. * @param selected
  223. * {@code true} if row should be selected; {@code false} if
  224. * not
  225. * @return {@code true} if selected status changed; {@code false} if not
  226. */
  227. protected boolean markAsSelected(RowHandle<JsonObject> row,
  228. boolean selected) {
  229. if (selected && !isSelected(row.getRow())) {
  230. row.getRow().put(GridState.JSONKEY_SELECTED, true);
  231. } else if (!selected && isSelected(row.getRow())) {
  232. row.getRow().remove(GridState.JSONKEY_SELECTED);
  233. } else {
  234. return false;
  235. }
  236. row.updateRow();
  237. if (isBeingBatchSelected()) {
  238. row.pin();
  239. }
  240. return true;
  241. }
  242. /**
  243. * {@inheritDoc}
  244. *
  245. * @return {@code false} if rows is empty, else {@code true}
  246. */
  247. @Override
  248. public boolean deselect(Collection<JsonObject> rows) {
  249. if (rows.isEmpty()) {
  250. return false;
  251. }
  252. for (JsonObject row : rows) {
  253. RowHandle<JsonObject> rowHandle = getRowHandle(row);
  254. if (markAsSelected(rowHandle, false)) {
  255. deselected.add(rowHandle);
  256. }
  257. }
  258. if (!isBeingBatchSelected()) {
  259. sendDeselected();
  260. }
  261. return true;
  262. }
  263. /**
  264. * Sends a deselect RPC call to server-side containing all deselected
  265. * rows. Unpins any pinned rows.
  266. */
  267. private void sendDeselected() {
  268. getRpcProxy(MultiSelectionModelServerRpc.class).deselect(
  269. getRowKeys(deselected));
  270. if (isBeingBatchSelected()) {
  271. for (RowHandle<JsonObject> row : deselected) {
  272. row.unpin();
  273. }
  274. }
  275. deselected.clear();
  276. }
  277. /**
  278. * Sends a select RPC call to server-side containing all selected rows.
  279. * Unpins any pinned rows.
  280. */
  281. private void sendSelected() {
  282. getRpcProxy(MultiSelectionModelServerRpc.class).select(
  283. getRowKeys(selected));
  284. if (isBeingBatchSelected()) {
  285. for (RowHandle<JsonObject> row : selected) {
  286. row.unpin();
  287. }
  288. }
  289. selected.clear();
  290. }
  291. private List<String> getRowKeys(Set<RowHandle<JsonObject>> handles) {
  292. List<String> keys = new ArrayList<String>();
  293. for (RowHandle<JsonObject> handle : handles) {
  294. keys.add(getRowKey(handle.getRow()));
  295. }
  296. return keys;
  297. }
  298. private Set<JsonObject> getRows(Set<RowHandle<JsonObject>> handles) {
  299. Set<JsonObject> rows = new HashSet<JsonObject>();
  300. for (RowHandle<JsonObject> handle : handles) {
  301. rows.add(handle.getRow());
  302. }
  303. return rows;
  304. }
  305. @Override
  306. public void startBatchSelect() {
  307. assert selected.isEmpty() && deselected.isEmpty() : "Row caches were not clear.";
  308. batchSelect = true;
  309. }
  310. @Override
  311. public void commitBatchSelect() {
  312. assert batchSelect : "Not batch selecting.";
  313. if (!selected.isEmpty()) {
  314. sendSelected();
  315. }
  316. if (!deselected.isEmpty()) {
  317. sendDeselected();
  318. }
  319. batchSelect = false;
  320. }
  321. @Override
  322. public boolean isBeingBatchSelected() {
  323. return batchSelect;
  324. }
  325. @Override
  326. public Collection<JsonObject> getSelectedRowsBatch() {
  327. return Collections.unmodifiableSet(getRows(selected));
  328. }
  329. @Override
  330. public Collection<JsonObject> getDeselectedRowsBatch() {
  331. return Collections.unmodifiableSet(getRows(deselected));
  332. }
  333. }
  334. }