/* * Copyright 2000-2016 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.v7.client.widget.grid.selection; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.v7.client.renderers.Renderer; import com.vaadin.v7.client.widgets.Grid; /** * Multi-row selection model. * * @author Vaadin Ltd * @since 7.4 */ public class SelectionModelMulti<T> extends AbstractRowHandleSelectionModel<T> implements SelectionModel.Multi.Batched<T>, HasUserSelectionAllowed<T> { private final LinkedHashSet<RowHandle<T>> selectedRows; private Renderer<Boolean> renderer; private Grid<T> grid; private boolean batchStarted = false; private final LinkedHashSet<RowHandle<T>> selectionBatch = new LinkedHashSet<RowHandle<T>>(); private final LinkedHashSet<RowHandle<T>> deselectionBatch = new LinkedHashSet<RowHandle<T>>(); /* Event handling for selection with space key */ private SpaceSelectHandler<T> spaceSelectHandler; private boolean userSelectionAllowed = true; public SelectionModelMulti() { grid = null; renderer = null; selectedRows = new LinkedHashSet<RowHandle<T>>(); } @Override public boolean isSelected(T row) { return isSelectedByHandle(grid.getDataSource().getHandle(row)); } @Override public Renderer<Boolean> getSelectionColumnRenderer() { return renderer; } @Override public void setGrid(Grid<T> grid) { if (this.grid != null && grid != null) { // Trying to replace grid throw new IllegalStateException( "Selection model is already attached to a grid. " + "Remove the selection model first from " + "the grid and then add it."); } this.grid = grid; if (this.grid != null) { spaceSelectHandler = new SpaceSelectHandler<T>(grid); this.renderer = new MultiSelectionRenderer<T>(grid); } else { spaceSelectHandler.removeHandler(); spaceSelectHandler = null; this.renderer = null; } } @Override public boolean select(T... rows) { if (rows == null) { throw new IllegalArgumentException("Rows cannot be null"); } return select(Arrays.asList(rows)); } @Override public boolean deselect(T... rows) { if (rows == null) { throw new IllegalArgumentException("Rows cannot be null"); } return deselect(Arrays.asList(rows)); } @Override public boolean deselectAll() { if (selectedRows.size() > 0) { @SuppressWarnings("unchecked") final LinkedHashSet<RowHandle<T>> selectedRowsClone = (LinkedHashSet<RowHandle<T>>) selectedRows .clone(); SelectionEvent<T> event = new SelectionEvent<T>(grid, null, getSelectedRows(), isBeingBatchSelected()); selectedRows.clear(); if (isBeingBatchSelected()) { selectionBatch.clear(); deselectionBatch.clear(); deselectionBatch.addAll(selectedRowsClone); } grid.fireEvent(event); return true; } return false; } @Override public boolean select(Collection<T> rows) { if (rows == null) { throw new IllegalArgumentException("Rows cannot be null"); } Set<T> added = new LinkedHashSet<T>(); for (T row : rows) { RowHandle<T> handle = grid.getDataSource().getHandle(row); if (selectByHandle(handle)) { added.add(row); } } if (added.size() > 0) { grid.fireEvent(new SelectionEvent<T>(grid, added, null, isBeingBatchSelected())); return true; } return false; } @Override public boolean deselect(Collection<T> rows) { if (rows == null) { throw new IllegalArgumentException("Rows cannot be null"); } Set<T> removed = new LinkedHashSet<T>(); for (T row : rows) { RowHandle<T> handle = grid.getDataSource().getHandle(row); if (deselectByHandle(handle)) { removed.add(row); } } if (removed.size() > 0) { grid.fireEvent(new SelectionEvent<T>(grid, null, removed, isBeingBatchSelected())); return true; } return false; } protected boolean isSelectedByHandle(RowHandle<T> handle) { return selectedRows.contains(handle); } @Override protected boolean selectByHandle(RowHandle<T> handle) { if (selectedRows.add(handle)) { handle.pin(); if (isBeingBatchSelected()) { deselectionBatch.remove(handle); selectionBatch.add(handle); } return true; } return false; } @Override protected boolean deselectByHandle(RowHandle<T> handle) { if (selectedRows.remove(handle)) { if (!isBeingBatchSelected()) { handle.unpin(); } else { selectionBatch.remove(handle); deselectionBatch.add(handle); } return true; } return false; } @Override public Collection<T> getSelectedRows() { Set<T> selected = new LinkedHashSet<T>(); for (RowHandle<T> handle : selectedRows) { selected.add(handle.getRow()); } return Collections.unmodifiableSet(selected); } @Override public void reset() { deselectAll(); } @Override public void startBatchSelect() { assert !isBeingBatchSelected() : "Batch has already been started"; batchStarted = true; } @Override public void commitBatchSelect() { assert isBeingBatchSelected() : "Batch was never started"; if (!isBeingBatchSelected()) { return; } batchStarted = false; final Collection<T> added = getSelectedRowsBatch(); selectionBatch.clear(); final Collection<T> removed = getDeselectedRowsBatch(); // unpin deselected rows for (RowHandle<T> handle : deselectionBatch) { handle.unpin(); } deselectionBatch.clear(); grid.fireEvent(new SelectionEvent<T>(grid, added, removed, isBeingBatchSelected())); } @Override public boolean isBeingBatchSelected() { return batchStarted; } @Override public Collection<T> getSelectedRowsBatch() { return rowHandlesToRows(selectionBatch); } @Override public Collection<T> getDeselectedRowsBatch() { return rowHandlesToRows(deselectionBatch); } private ArrayList<T> rowHandlesToRows(Collection<RowHandle<T>> rowHandles) { ArrayList<T> rows = new ArrayList<T>(rowHandles.size()); for (RowHandle<T> handle : rowHandles) { rows.add(handle.getRow()); } return rows; } @Override public boolean isUserSelectionAllowed() { return userSelectionAllowed; } @Override public void setUserSelectionAllowed(boolean userSelectionAllowed) { this.userSelectionAllowed = userSelectionAllowed; } }