2 * Copyright 2000-2018 Vaadin Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
16 package com.vaadin.v7.client.connectors;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashSet;
23 import java.util.List;
25 import java.util.logging.Logger;
27 import com.google.gwt.event.shared.HandlerRegistration;
28 import com.google.gwt.user.client.ui.CheckBox;
29 import com.vaadin.client.ServerConnector;
30 import com.vaadin.client.annotations.OnStateChange;
31 import com.vaadin.client.data.DataSource;
32 import com.vaadin.client.data.DataSource.RowHandle;
33 import com.vaadin.shared.Range;
34 import com.vaadin.shared.ui.Connect;
35 import com.vaadin.v7.client.renderers.ComplexRenderer;
36 import com.vaadin.v7.client.renderers.Renderer;
37 import com.vaadin.v7.client.widget.grid.DataAvailableEvent;
38 import com.vaadin.v7.client.widget.grid.DataAvailableHandler;
39 import com.vaadin.v7.client.widget.grid.events.SelectAllEvent;
40 import com.vaadin.v7.client.widget.grid.events.SelectAllHandler;
41 import com.vaadin.v7.client.widget.grid.selection.HasUserSelectionAllowed;
42 import com.vaadin.v7.client.widget.grid.selection.MultiSelectionRenderer;
43 import com.vaadin.v7.client.widget.grid.selection.SelectionModel;
44 import com.vaadin.v7.client.widget.grid.selection.SelectionModel.Multi;
45 import com.vaadin.v7.client.widget.grid.selection.SpaceSelectHandler;
46 import com.vaadin.v7.client.widgets.Grid;
47 import com.vaadin.v7.client.widgets.Grid.Column;
48 import com.vaadin.v7.client.widgets.Grid.HeaderCell;
49 import com.vaadin.v7.client.widgets.Grid.SelectionColumn;
50 import com.vaadin.v7.shared.ui.grid.GridState;
51 import com.vaadin.v7.shared.ui.grid.selection.MultiSelectionModelServerRpc;
52 import com.vaadin.v7.shared.ui.grid.selection.MultiSelectionModelState;
53 import com.vaadin.v7.ui.Grid.MultiSelectionModel;
55 import elemental.json.JsonObject;
58 * Connector for server-side {@link MultiSelectionModel}.
63 @Connect(MultiSelectionModel.class)
64 public class MultiSelectionModelConnector extends
65 AbstractSelectionModelConnector<SelectionModel.Multi<JsonObject>> {
67 private Multi<JsonObject> selectionModel = createSelectionModel();
68 private SpaceSelectHandler<JsonObject> spaceHandler;
71 protected void extend(ServerConnector target) {
72 getGrid().setSelectionModel(selectionModel);
73 spaceHandler = new SpaceSelectHandler<JsonObject>(getGrid());
77 public void onUnregister() {
78 spaceHandler.removeHandler();
82 protected Multi<JsonObject> createSelectionModel() {
83 return new MultiSelectionModel();
87 public MultiSelectionModelState getState() {
88 return (MultiSelectionModelState) super.getState();
91 @OnStateChange("allSelected")
92 void updateSelectAllCheckbox() {
93 if (selectionModel.getSelectionColumnRenderer() != null) {
94 HeaderCell cell = getGrid().getDefaultHeaderRow()
95 .getCell(getGrid().getColumn(0));
96 CheckBox widget = (CheckBox) cell.getWidget();
97 widget.setValue(getState().allSelected, false);
101 @OnStateChange("userSelectionAllowed")
102 void updateUserSelectionAllowed() {
103 if (selectionModel instanceof HasUserSelectionAllowed) {
104 ((HasUserSelectionAllowed) selectionModel)
105 .setUserSelectionAllowed(getState().userSelectionAllowed);
107 getLogger().warning("userSelectionAllowed set to "
108 + getState().userSelectionAllowed
109 + " but the selection model does not implement "
110 + HasUserSelectionAllowed.class.getSimpleName());
114 private static Logger getLogger() {
115 return Logger.getLogger(MultiSelectionModelConnector.class.getName());
119 * The default multi selection model used for this connector.
122 protected class MultiSelectionModel extends AbstractSelectionModel
123 implements SelectionModel.Multi.Batched<JsonObject>,
124 HasUserSelectionAllowed<JsonObject> {
126 private ComplexRenderer<Boolean> renderer = null;
127 private Set<RowHandle<JsonObject>> selected = new HashSet<RowHandle<JsonObject>>();
128 private Set<RowHandle<JsonObject>> deselected = new HashSet<RowHandle<JsonObject>>();
129 private HandlerRegistration selectAll;
130 private HandlerRegistration dataAvailable;
131 private Range availableRows;
132 private boolean batchSelect = false;
133 private boolean userSelectionAllowed = true;
136 public void setGrid(Grid<JsonObject> grid) {
139 renderer = createSelectionColumnRenderer(grid);
140 selectAll = getGrid().addSelectAllHandler(
141 new SelectAllHandler<JsonObject>() {
144 public void onSelectAll(
145 SelectAllEvent<JsonObject> event) {
149 dataAvailable = getGrid()
150 .addDataAvailableHandler(new DataAvailableHandler() {
153 public void onDataAvailable(
154 DataAvailableEvent event) {
155 availableRows = event.getAvailableRows();
158 } else if (renderer != null) {
159 selectAll.removeHandler();
160 dataAvailable.removeHandler();
166 * Creates a selection column renderer. This method can be overridden to
167 * use a custom renderer or use {@code null} to disable the selection
171 * the grid for this selection model
172 * @return selection column renderer or {@code null} if not needed
174 protected ComplexRenderer<Boolean> createSelectionColumnRenderer(
175 Grid<JsonObject> grid) {
176 return new MultiSelectionRenderer<JsonObject>(grid);
180 * Selects all available rows, sends request to server to select
183 public void selectAll() {
184 assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
186 DataSource<JsonObject> dataSource = getGrid().getDataSource();
187 for (int i = availableRows.getStart(); i < availableRows
189 final JsonObject row = dataSource.getRow(i);
191 RowHandle<JsonObject> handle = dataSource.getHandle(row);
192 markAsSelected(handle, true);
196 getRpcProxy(MultiSelectionModelServerRpc.class).selectAll();
200 public Renderer<Boolean> getSelectionColumnRenderer() {
207 * @return {@code false} if rows is empty, else {@code true}
210 public boolean select(JsonObject... rows) {
211 return select(Arrays.asList(rows));
217 * @return {@code false} if rows is empty, else {@code true}
220 public boolean deselect(JsonObject... rows) {
221 return deselect(Arrays.asList(rows));
227 * @return always {@code true}
230 public boolean deselectAll() {
231 assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
233 DataSource<JsonObject> dataSource = getGrid().getDataSource();
234 for (int i = availableRows.getStart(); i < availableRows
236 final JsonObject row = dataSource.getRow(i);
238 RowHandle<JsonObject> handle = dataSource.getHandle(row);
239 markAsSelected(handle, false);
243 getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll();
251 * @return {@code false} if rows is empty, else {@code true}
254 public boolean select(Collection<JsonObject> rows) {
255 if (rows.isEmpty()) {
259 for (JsonObject row : rows) {
260 RowHandle<JsonObject> rowHandle = getRowHandle(row);
261 if (markAsSelected(rowHandle, true)) {
262 selected.add(rowHandle);
266 if (!isBeingBatchSelected()) {
273 * Marks the given row to be selected or deselected. Returns true if the
274 * value actually changed.
276 * Note: If selection model is in batch select state, the row will be
282 * {@code true} if row should be selected; {@code false} if
284 * @return {@code true} if selected status changed; {@code false} if not
286 protected boolean markAsSelected(RowHandle<JsonObject> row,
288 if (selected && !isSelected(row.getRow())) {
289 row.getRow().put(GridState.JSONKEY_SELECTED, true);
290 } else if (!selected && isSelected(row.getRow())) {
291 row.getRow().remove(GridState.JSONKEY_SELECTED);
298 if (isBeingBatchSelected()) {
307 * @return {@code false} if rows is empty, else {@code true}
310 public boolean deselect(Collection<JsonObject> rows) {
311 if (rows.isEmpty()) {
315 for (JsonObject row : rows) {
316 RowHandle<JsonObject> rowHandle = getRowHandle(row);
317 if (markAsSelected(rowHandle, false)) {
318 deselected.add(rowHandle);
322 if (!isBeingBatchSelected()) {
329 * Sends a deselect RPC call to server-side containing all deselected
330 * rows. Unpins any pinned rows.
332 private void sendDeselected() {
333 getRpcProxy(MultiSelectionModelServerRpc.class)
334 .deselect(getRowKeys(deselected));
336 if (isBeingBatchSelected()) {
337 for (RowHandle<JsonObject> row : deselected) {
346 * Sends a select RPC call to server-side containing all selected rows.
347 * Unpins any pinned rows.
349 private void sendSelected() {
350 getRpcProxy(MultiSelectionModelServerRpc.class)
351 .select(getRowKeys(selected));
353 if (isBeingBatchSelected()) {
354 for (RowHandle<JsonObject> row : selected) {
362 private List<String> getRowKeys(Set<RowHandle<JsonObject>> handles) {
363 List<String> keys = new ArrayList<String>();
364 for (RowHandle<JsonObject> handle : handles) {
365 keys.add(getRowKey(handle.getRow()));
370 private Set<JsonObject> getRows(Set<RowHandle<JsonObject>> handles) {
371 Set<JsonObject> rows = new HashSet<JsonObject>();
372 for (RowHandle<JsonObject> handle : handles) {
373 rows.add(handle.getRow());
379 public void startBatchSelect() {
380 assert selected.isEmpty()
381 && deselected.isEmpty() : "Row caches were not clear.";
386 public void commitBatchSelect() {
387 assert batchSelect : "Not batch selecting.";
388 if (!selected.isEmpty()) {
392 if (!deselected.isEmpty()) {
399 public boolean isBeingBatchSelected() {
404 public Collection<JsonObject> getSelectedRowsBatch() {
405 return Collections.unmodifiableSet(getRows(selected));
409 public Collection<JsonObject> getDeselectedRowsBatch() {
410 return Collections.unmodifiableSet(getRows(deselected));
414 public boolean isUserSelectionAllowed() {
415 return userSelectionAllowed;
419 public void setUserSelectionAllowed(boolean userSelectionAllowed) {
420 this.userSelectionAllowed = userSelectionAllowed;
421 for (Column<?, JsonObject> c : getGrid().getColumns()) {
422 if (c instanceof SelectionColumn) {
423 ((SelectionColumn) c)
424 .setUserSelectionAllowed(userSelectionAllowed);