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