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 14KB

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