]> source.dussan.org Git - vaadin-framework.git/blob
11503685a24cfb752a466b3c2345979b4f041d56
[vaadin-framework.git] /
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
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;
24 import java.util.Set;
25 import java.util.logging.Logger;
26
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;
54
55 import elemental.json.JsonObject;
56
57 /**
58  * Connector for server-side {@link MultiSelectionModel}.
59  *
60  * @since 7.6
61  * @author Vaadin Ltd
62  */
63 @Connect(MultiSelectionModel.class)
64 public class MultiSelectionModelConnector extends
65         AbstractSelectionModelConnector<SelectionModel.Multi<JsonObject>> {
66
67     private Multi<JsonObject> selectionModel = createSelectionModel();
68     private SpaceSelectHandler<JsonObject> spaceHandler;
69
70     @Override
71     protected void extend(ServerConnector target) {
72         getGrid().setSelectionModel(selectionModel);
73         spaceHandler = new SpaceSelectHandler<JsonObject>(getGrid());
74     }
75
76     @Override
77     public void onUnregister() {
78         spaceHandler.removeHandler();
79     }
80
81     @Override
82     protected Multi<JsonObject> createSelectionModel() {
83         return new MultiSelectionModel();
84     }
85
86     @Override
87     public MultiSelectionModelState getState() {
88         return (MultiSelectionModelState) super.getState();
89     }
90
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);
98         }
99     }
100
101     @OnStateChange("userSelectionAllowed")
102     void updateUserSelectionAllowed() {
103         if (selectionModel instanceof HasUserSelectionAllowed) {
104             ((HasUserSelectionAllowed) selectionModel)
105                     .setUserSelectionAllowed(getState().userSelectionAllowed);
106         } else {
107             getLogger().warning("userSelectionAllowed set to "
108                     + getState().userSelectionAllowed
109                     + " but the selection model does not implement "
110                     + HasUserSelectionAllowed.class.getSimpleName());
111         }
112     }
113
114     private static Logger getLogger() {
115         return Logger.getLogger(MultiSelectionModelConnector.class.getName());
116     }
117
118     /**
119      * The default multi selection model used for this connector.
120      *
121      */
122     protected class MultiSelectionModel extends AbstractSelectionModel
123             implements SelectionModel.Multi.Batched<JsonObject>,
124             HasUserSelectionAllowed<JsonObject> {
125
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;
134
135         @Override
136         public void setGrid(Grid<JsonObject> grid) {
137             super.setGrid(grid);
138             if (grid != null) {
139                 renderer = createSelectionColumnRenderer(grid);
140                 selectAll = getGrid().addSelectAllHandler(
141                         new SelectAllHandler<JsonObject>() {
142
143                             @Override
144                             public void onSelectAll(
145                                     SelectAllEvent<JsonObject> event) {
146                                 selectAll();
147                             }
148                         });
149                 dataAvailable = getGrid()
150                         .addDataAvailableHandler(new DataAvailableHandler() {
151
152                             @Override
153                             public void onDataAvailable(
154                                     DataAvailableEvent event) {
155                                 availableRows = event.getAvailableRows();
156                             }
157                         });
158             } else if (renderer != null) {
159                 selectAll.removeHandler();
160                 dataAvailable.removeHandler();
161                 renderer = null;
162             }
163         }
164
165         /**
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
168          * column.
169          *
170          * @param grid
171          *            the grid for this selection model
172          * @return selection column renderer or {@code null} if not needed
173          */
174         protected ComplexRenderer<Boolean> createSelectionColumnRenderer(
175                 Grid<JsonObject> grid) {
176             return new MultiSelectionRenderer<JsonObject>(grid);
177         }
178
179         /**
180          * Selects all available rows, sends request to server to select
181          * everything.
182          */
183         public void selectAll() {
184             assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
185
186             DataSource<JsonObject> dataSource = getGrid().getDataSource();
187             for (int i = availableRows.getStart(); i < availableRows
188                     .getEnd(); ++i) {
189                 final JsonObject row = dataSource.getRow(i);
190                 if (row != null) {
191                     RowHandle<JsonObject> handle = dataSource.getHandle(row);
192                     markAsSelected(handle, true);
193                 }
194             }
195
196             getRpcProxy(MultiSelectionModelServerRpc.class).selectAll();
197         }
198
199         @Override
200         public Renderer<Boolean> getSelectionColumnRenderer() {
201             return renderer;
202         }
203
204         /**
205          * {@inheritDoc}
206          *
207          * @return {@code false} if rows is empty, else {@code true}
208          */
209         @Override
210         public boolean select(JsonObject... rows) {
211             return select(Arrays.asList(rows));
212         }
213
214         /**
215          * {@inheritDoc}
216          *
217          * @return {@code false} if rows is empty, else {@code true}
218          */
219         @Override
220         public boolean deselect(JsonObject... rows) {
221             return deselect(Arrays.asList(rows));
222         }
223
224         /**
225          * {@inheritDoc}
226          *
227          * @return always {@code true}
228          */
229         @Override
230         public boolean deselectAll() {
231             assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection.";
232
233             DataSource<JsonObject> dataSource = getGrid().getDataSource();
234             for (int i = availableRows.getStart(); i < availableRows
235                     .getEnd(); ++i) {
236                 final JsonObject row = dataSource.getRow(i);
237                 if (row != null) {
238                     RowHandle<JsonObject> handle = dataSource.getHandle(row);
239                     markAsSelected(handle, false);
240                 }
241             }
242
243             getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll();
244
245             return true;
246         }
247
248         /**
249          * {@inheritDoc}
250          *
251          * @return {@code false} if rows is empty, else {@code true}
252          */
253         @Override
254         public boolean select(Collection<JsonObject> rows) {
255             if (rows.isEmpty()) {
256                 return false;
257             }
258
259             for (JsonObject row : rows) {
260                 RowHandle<JsonObject> rowHandle = getRowHandle(row);
261                 if (markAsSelected(rowHandle, true)) {
262                     selected.add(rowHandle);
263                 }
264             }
265
266             if (!isBeingBatchSelected()) {
267                 sendSelected();
268             }
269             return true;
270         }
271
272         /**
273          * Marks the given row to be selected or deselected. Returns true if the
274          * value actually changed.
275          * <p>
276          * Note: If selection model is in batch select state, the row will be
277          * pinned on select.
278          *
279          * @param row
280          *            row handle
281          * @param selected
282          *            {@code true} if row should be selected; {@code false} if
283          *            not
284          * @return {@code true} if selected status changed; {@code false} if not
285          */
286         protected boolean markAsSelected(RowHandle<JsonObject> row,
287                 boolean selected) {
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);
292             } else {
293                 return false;
294             }
295
296             row.updateRow();
297
298             if (isBeingBatchSelected()) {
299                 row.pin();
300             }
301             return true;
302         }
303
304         /**
305          * {@inheritDoc}
306          *
307          * @return {@code false} if rows is empty, else {@code true}
308          */
309         @Override
310         public boolean deselect(Collection<JsonObject> rows) {
311             if (rows.isEmpty()) {
312                 return false;
313             }
314
315             for (JsonObject row : rows) {
316                 RowHandle<JsonObject> rowHandle = getRowHandle(row);
317                 if (markAsSelected(rowHandle, false)) {
318                     deselected.add(rowHandle);
319                 }
320             }
321
322             if (!isBeingBatchSelected()) {
323                 sendDeselected();
324             }
325             return true;
326         }
327
328         /**
329          * Sends a deselect RPC call to server-side containing all deselected
330          * rows. Unpins any pinned rows.
331          */
332         private void sendDeselected() {
333             getRpcProxy(MultiSelectionModelServerRpc.class)
334                     .deselect(getRowKeys(deselected));
335
336             if (isBeingBatchSelected()) {
337                 for (RowHandle<JsonObject> row : deselected) {
338                     row.unpin();
339                 }
340             }
341
342             deselected.clear();
343         }
344
345         /**
346          * Sends a select RPC call to server-side containing all selected rows.
347          * Unpins any pinned rows.
348          */
349         private void sendSelected() {
350             getRpcProxy(MultiSelectionModelServerRpc.class)
351                     .select(getRowKeys(selected));
352
353             if (isBeingBatchSelected()) {
354                 for (RowHandle<JsonObject> row : selected) {
355                     row.unpin();
356                 }
357             }
358
359             selected.clear();
360         }
361
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()));
366             }
367             return keys;
368         }
369
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());
374             }
375             return rows;
376         }
377
378         @Override
379         public void startBatchSelect() {
380             assert selected.isEmpty()
381                     && deselected.isEmpty() : "Row caches were not clear.";
382             batchSelect = true;
383         }
384
385         @Override
386         public void commitBatchSelect() {
387             assert batchSelect : "Not batch selecting.";
388             if (!selected.isEmpty()) {
389                 sendSelected();
390             }
391
392             if (!deselected.isEmpty()) {
393                 sendDeselected();
394             }
395             batchSelect = false;
396         }
397
398         @Override
399         public boolean isBeingBatchSelected() {
400             return batchSelect;
401         }
402
403         @Override
404         public Collection<JsonObject> getSelectedRowsBatch() {
405             return Collections.unmodifiableSet(getRows(selected));
406         }
407
408         @Override
409         public Collection<JsonObject> getDeselectedRowsBatch() {
410             return Collections.unmodifiableSet(getRows(deselected));
411         }
412
413         @Override
414         public boolean isUserSelectionAllowed() {
415             return userSelectionAllowed;
416         }
417
418         @Override
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);
425                 }
426             }
427         }
428     }
429 }