diff --git a/client/src/main/java/com/vaadin/client/connectors/AbstractListingConnector.java b/client/src/main/java/com/vaadin/client/connectors/AbstractListingConnector.java index c00807ed2c6..938df199da0 100644 --- a/client/src/main/java/com/vaadin/client/connectors/AbstractListingConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/AbstractListingConnector.java @@ -33,7 +33,7 @@ public abstract class AbstractListingConnector private DataSource dataSource = null; - private SelectionModel selectionModel = null; + private SelectionModel selectionModel = null; @Override public void setDataSource(DataSource dataSource) { @@ -47,15 +47,15 @@ public DataSource getDataSource() { /** * Sets the selection model to use. Passing {@code null} disables selection. - * + * * @param selectionModel * the selection model or null to disable */ - public void setSelectionModel(SelectionModel selectionModel) { + public void setSelectionModel(SelectionModel selectionModel) { this.selectionModel = selectionModel; } - public SelectionModel getSelectionModel() { + public SelectionModel getSelectionModel() { return selectionModel; } } diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java index 2259f47647d..31f6cc38ab8 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java @@ -31,10 +31,13 @@ import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.widget.grid.selection.ClickSelectHandler; +import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; import com.vaadin.client.widget.grid.sort.SortEvent; import com.vaadin.client.widget.grid.sort.SortOrder; import com.vaadin.client.widgets.Grid; import com.vaadin.client.widgets.Grid.Column; +import com.vaadin.shared.data.selection.SelectionModel; +import com.vaadin.shared.data.selection.SelectionModel.Single; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.GridServerRpc; @@ -56,6 +59,8 @@ public class GridConnector extends AbstractListingConnector private Map, String> columnToIdMap = new HashMap<>(); /* Child component list for HasComponentsConnector */ private List childComponents; + private SpaceSelectHandler spaceSelectHandler; + private ClickSelectHandler clickSelectHandler; @Override public Grid getWidget() { @@ -66,7 +71,8 @@ public Grid getWidget() { protected void init() { super.init(); - new ClickSelectHandler<>(getWidget()); + // Default selection style is space key. + spaceSelectHandler = new SpaceSelectHandler(getWidget()); getWidget().addSortHandler(this::handleSortEvent); layout(); @@ -74,9 +80,23 @@ protected void init() { @Override public void setDataSource(DataSource dataSource) { + super.setDataSource(dataSource); getWidget().setDataSource(dataSource); } + @Override + public void setSelectionModel(SelectionModel selectionModel) { + removeClickHandler(); + + super.setSelectionModel(selectionModel); + getWidget().setSelectionModel(selectionModel); + + if (selectionModel instanceof Single) { + // Single selection should be moved by a click. + clickSelectHandler = new ClickSelectHandler<>(getWidget()); + } + } + /** * Adds a column to the Grid widget. For each column a communication id * stored for client to server communication. @@ -112,6 +132,12 @@ public void onUnregister() { super.onUnregister(); columnToIdMap.clear(); + removeClickHandler(); + + if (spaceSelectHandler != null) { + spaceSelectHandler.removeHandler(); + spaceSelectHandler = null; + } } @Override @@ -177,4 +203,11 @@ public HandlerRegistration addConnectorHierarchyChangeHandler( public GridState getState() { return (GridState) super.getState(); } + + private void removeClickHandler() { + if (clickSelectHandler != null) { + clickSelectHandler.removeHandler(); + clickSelectHandler = null; + } + } } diff --git a/client/src/main/java/com/vaadin/client/connectors/selection/AbstractSelectionConnector.java b/client/src/main/java/com/vaadin/client/connectors/selection/AbstractSelectionConnector.java index b295fb0cc36..7edc944a224 100644 --- a/client/src/main/java/com/vaadin/client/connectors/selection/AbstractSelectionConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/selection/AbstractSelectionConnector.java @@ -1,12 +1,12 @@ /* * 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 @@ -18,19 +18,22 @@ import com.vaadin.client.ServerConnector; import com.vaadin.client.connectors.AbstractListingConnector; import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.selection.SelectionModel; +import elemental.json.JsonObject; + /** * The client-side connector for selection extensions. - * + * * @author Vaadin Ltd. - * + * * @since */ -public abstract class AbstractSelectionConnector extends - AbstractExtensionConnector { +public abstract class AbstractSelectionConnector + extends AbstractExtensionConnector { - private SelectionModel model = null; + private SelectionModel model = null; @Override protected void extend(ServerConnector target) { @@ -45,10 +48,10 @@ protected void extend(ServerConnector target) { /** * Creates a selection model object to be used by the Connector. - * + * * @return created selection model */ - protected abstract SelectionModel createSelectionModel(); + protected abstract SelectionModel createSelectionModel(); @Override public AbstractListingConnector getParent() { @@ -57,10 +60,41 @@ public AbstractListingConnector getParent() { /** * Returns the client-side selection model associated with this connector. - * + * * @return the selection model in use */ - protected SelectionModel getSelectionModel() { + protected SelectionModel getSelectionModel() { return model; } + + /** + * Gets the selected state from a given json object. This is a helper method + * for selection model connectors. + * + * @param item + * a json object + * @return {@code true} if the json object is marked as selected; + * {@code false} if not + */ + public static boolean isItemSelected(JsonObject item) { + return item.hasKey(DataCommunicatorConstants.SELECTED) + && item.getBoolean(DataCommunicatorConstants.SELECTED); + } + + /** + * Gets the item key from given json object. This is a helper method for + * selection model connectors. + * + * @param item + * a json object + * @return item key; {@code null} if there is no key + */ + public static String getKey(JsonObject item) { + if (item.hasKey(DataCommunicatorConstants.KEY)) { + return item.getString(DataCommunicatorConstants.KEY); + } else { + return null; + } + } + } diff --git a/client/src/main/java/com/vaadin/client/connectors/selection/SingleSelectionConnector.java b/client/src/main/java/com/vaadin/client/connectors/selection/SingleSelectionConnector.java index 70844d1ae75..912b20310e4 100644 --- a/client/src/main/java/com/vaadin/client/connectors/selection/SingleSelectionConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/selection/SingleSelectionConnector.java @@ -1,12 +1,12 @@ /* * 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 @@ -23,18 +23,19 @@ import com.vaadin.shared.data.selection.SelectionServerRpc; import com.vaadin.shared.ui.Connect; +import elemental.json.JsonObject; + /** * A connector for single selection extensions. - * + * * @author Vaadin Ltd. */ @Connect(com.vaadin.data.selection.SingleSelection.class) public class SingleSelectionConnector extends AbstractSelectionConnector { - private static class SingleSelection implements - SelectionModel.Single { + private static class SingleSelection + implements SelectionModel.Single { - private String value; private SelectionServerRpc rpc; SingleSelection(SelectionServerRpc rpc) { @@ -42,29 +43,28 @@ private static class SingleSelection implements } @Override - public void select(String item) { - if (item != null && !item.equals(value)) { - rpc.select(item); - value = item; + public void select(JsonObject item) { + if (!isSelected(item)) { + rpc.select(getKey(item)); } } @Override - public void deselect(String item) { - if (item != null && item.equals(value)) { - rpc.deselect(item); - value = null; + public void deselect(JsonObject item) { + if (isSelected(item)) { + rpc.deselect(getKey(item)); } } @Override - public boolean isSelected(String item) { - return value != null && value.equals(item); + public boolean isSelected(JsonObject item) { + return isItemSelected(item); } @Override - public Optional getSelectedItem() { - return Optional.ofNullable(value); + public Optional getSelectedItem() { + throw new UnsupportedOperationException( + "A client-side selection model does not know the full selection"); } } @@ -85,7 +85,7 @@ protected void extend(ServerConnector target) { } @Override - protected SelectionModel createSelectionModel() { + protected SelectionModel createSelectionModel() { return new SingleSelection(getRpcProxy(SelectionServerRpc.class)); } } diff --git a/client/src/main/java/com/vaadin/client/widget/grid/AutoScroller.java b/client/src/main/java/com/vaadin/client/widget/grid/AutoScroller.java index db77701b92b..826105d6237 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/AutoScroller.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/AutoScroller.java @@ -27,6 +27,7 @@ import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.vaadin.client.WidgetUtil; +import com.vaadin.client.widget.grid.selection.SelectionModelWithSelectionColumn; import com.vaadin.client.widgets.Grid; /** @@ -625,8 +626,8 @@ public double getFrozenColumnsWidth() { private int getRealFrozenColumnCount() { if (grid.getFrozenColumnCount() < 0) { return 0; - } else if (grid.getSelectionModel() - .getSelectionColumnRenderer() != null) { + } else if (grid + .getSelectionModel() instanceof SelectionModelWithSelectionColumn) { // includes the selection column return grid.getFrozenColumnCount() + 1; } else { diff --git a/client/src/main/java/com/vaadin/client/widget/grid/datasources/ListDataSource.java b/client/src/main/java/com/vaadin/client/widget/grid/datasources/ListDataSource.java index b5b6f42f3a0..9767a230bbe 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/datasources/ListDataSource.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/datasources/ListDataSource.java @@ -452,7 +452,7 @@ public SelectAllHandler getSelectAllHandler() { return new SelectAllHandler() { @Override public void onSelectAll(SelectAllEvent event) { - event.getSelectionModel().select(asList()); + asList().forEach(event.getSelectionModel()::select); } }; } @@ -461,5 +461,4 @@ private Stream getHandlers() { Set copy = new LinkedHashSet<>(changeHandlers); return copy.stream(); } - } diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java index a21adfd2a8c..cd86dbe9931 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/SelectAllEvent.java @@ -16,7 +16,7 @@ package com.vaadin.client.widget.grid.events; import com.google.gwt.event.shared.GwtEvent; -import com.vaadin.client.widget.grid.selection.SelectionModel; +import com.vaadin.shared.data.selection.SelectionModel; /** * A select all event, fired by the Grid when it needs all rows in data source diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/AbstractRowHandleSelectionModel.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/AbstractRowHandleSelectionModel.java deleted file mode 100644 index 9bc53979c54..00000000000 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/AbstractRowHandleSelectionModel.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.client.widget.grid.selection; - -import com.vaadin.client.data.DataSource.RowHandle; - -/** - * An abstract class that adds a consistent API for common methods that's needed - * by Vaadin's server-based selection models to work. - *

- * Note: This should be an interface instead of an abstract class, if - * only we could define protected methods in an interface. - * - * @author Vaadin Ltd - * @param - * The grid's row type - * @since 7.4 - */ -public abstract class AbstractRowHandleSelectionModel - implements SelectionModel { - /** - * Select a row, based on its - * {@link com.vaadin.client.data.DataSource.RowHandle RowHandle}. - *

- * Note: this method may not fire selection change events. - * - * @param handle - * the handle to select by - * @return true iff the selection state was changed by this - * call - * @throws UnsupportedOperationException - * if the selection model does not support either handles or - * selection - */ - protected abstract boolean selectByHandle(RowHandle handle); - - /** - * Deselect a row, based on its - * {@link com.vaadin.client.data.DataSource.RowHandle RowHandle}. - *

- * Note: this method may not fire selection change events. - * - * @param handle - * the handle to deselect by - * @return true iff the selection state was changed by this - * call - * @throws UnsupportedOperationException - * if the selection model does not support either handles or - * deselection - */ - protected abstract boolean deselectByHandle(RowHandle handle) - throws UnsupportedOperationException; -} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java index 2ae8b0579d2..e1f9ebb18ed 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/MultiSelectionRenderer.java @@ -44,7 +44,6 @@ import com.vaadin.client.widget.grid.RendererCellReference; import com.vaadin.client.widget.grid.events.GridEnabledEvent; import com.vaadin.client.widget.grid.events.GridEnabledHandler; -import com.vaadin.client.widget.grid.selection.SelectionModel.Multi.Batched; import com.vaadin.client.widgets.Grid; /** @@ -523,13 +522,6 @@ public void onPreviewNativeEvent(final NativePreviewEvent event) { private int gradientArea; public void start(int logicalRowIndex) { - - SelectionModel model = grid.getSelectionModel(); - if (model instanceof Batched) { - Batched batchedModel = (Batched) model; - batchedModel.startBatchSelect(); - } - /* * bounds are updated whenever the autoscroll cycle starts, to make * sure that the widget hasn't changed in size, moved around, or @@ -576,12 +568,6 @@ public void stop() { autoScroller = null; } - SelectionModel model = grid.getSelectionModel(); - if (model instanceof Batched) { - Batched batchedModel = (Batched) model; - batchedModel.commitBatchSelect(); - } - removeNativeHandler(); } } diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java deleted file mode 100644 index 3d2c7d4873a..00000000000 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModel.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.client.widget.grid.selection; - -import java.util.Collection; - -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widgets.Grid; - -/** - * Common interface for all selection models. - *

- * Selection models perform tracking of selected rows in the Grid, as well as - * dispatching events when the selection state changes. - * - * @author Vaadin Ltd - * @param - * Grid's row type - * @since 7.4 - */ -public interface SelectionModel { - - /** - * Return true if the provided row is considered selected under the - * implementing selection model. - * - * @param row - * row object instance - * @return true, if the row given as argument is considered - * selected. - */ - public boolean isSelected(T row); - - /** - * Return the {@link Renderer} responsible for rendering the selection - * column. - * - * @return a renderer instance. If null is returned, a selection column will - * not be drawn. - */ - public Renderer getSelectionColumnRenderer(); - - /** - * Tells this SelectionModel which Grid it belongs to. - *

- * Implementations are free to have this be a no-op. This method is called - * internally by Grid. - * - * @param grid - * a {@link Grid} instance; null when removing from - * Grid - */ - public void setGrid(Grid grid); - - /** - * Resets the SelectionModel to the initial state. - *

- * This method can be called internally, for example, when the attached - * Grid's data source changes. - */ - public void reset(); - - /** - * Returns a Collection containing all selected rows. - * - * @return a non-null collection. - */ - public Collection getSelectedRows(); - - /** - * Selection model that allows a maximum of one row to be selected at any - * one time. - * - * @param - * type parameter corresponding with Grid row type - */ - public interface Single extends SelectionModel { - - /** - * Selects a row. - * - * @param row - * a {@link Grid} row object - * @return true, if this row as not previously selected. - */ - public boolean select(T row); - - /** - * Deselects a row. - *

- * This is a no-op unless {@link row} is the currently selected row. - * - * @param row - * a {@link Grid} row object - * @return true, if the currently selected row was deselected. - */ - public boolean deselect(T row); - - /** - * Returns the currently selected row. - * - * @return a {@link Grid} row object or null, if nothing is selected. - */ - public T getSelectedRow(); - - /** - * Sets whether it's allowed to deselect the selected row through the - * UI. Deselection is allowed by default. - * - * @param deselectAllowed - * true if the selected row can be deselected - * without selecting another row instead; otherwise - * false. - */ - public void setDeselectAllowed(boolean deselectAllowed); - - /** - * Sets whether it's allowed to deselect the selected row through the - * UI. - * - * @return true if deselection is allowed; otherwise - * false - */ - public boolean isDeselectAllowed(); - - } - - /** - * Selection model that allows for several rows to be selected at once. - * - * @param - * type parameter corresponding with Grid row type - */ - public interface Multi extends SelectionModel { - - /** - * A multi selection model that can send selections and deselections in - * a batch, instead of committing them one-by-one. - * - * @param - * type parameter corresponding with Grid row type - */ - public interface Batched extends Multi { - /** - * Starts a batch selection. - *

- * Any commands to any select or deselect method will be batched - * into one, and a final selection event will be fired when - * {@link #commitBatchSelect()} is called. - *

- * Note: {@link SelectionEvent SelectionChangeEvents} will - * still be fired for each selection/deselection. You should check - * whether the event is a part of a batch or not with - * {@link SelectionEvent#isBatchedSelection()}. - */ - public void startBatchSelect(); - - /** - * Commits and ends a batch selection. - *

- * Any and all selections and deselections since the last invocation - * of {@link #startBatchSelect()} will be fired at once as one - * collated {@link SelectionEvent}. - */ - public void commitBatchSelect(); - - /** - * Checks whether or not a batch has been started. - * - * @return true iff a batch has been started - */ - public boolean isBeingBatchSelected(); - - /** - * Gets all the rows that would become selected in this batch. - * - * @return a collection of the rows that would become selected - */ - public Collection getSelectedRowsBatch(); - - /** - * Gets all the rows that would become deselected in this batch. - * - * @return a collection of the rows that would become deselected - */ - public Collection getDeselectedRowsBatch(); - } - - /** - * Selects one or more rows. - * - * @param rows - * {@link Grid} row objects - * @return true, if the set of selected rows was changed. - */ - public boolean select(T... rows); - - /** - * Deselects one or more rows. - * - * @param rows - * Grid row objects - * @return true, if the set of selected rows was changed. - */ - public boolean deselect(T... rows); - - /** - * De-selects all rows. - * - * @return true, if any row was previously selected. - */ - public boolean deselectAll(); - - /** - * Select all rows in a {@link Collection}. - * - * @param rows - * a collection of Grid row objects - * @return true, if the set of selected rows was changed. - */ - public boolean select(Collection rows); - - /** - * Deselect all rows in a {@link Collection}. - * - * @param rows - * a collection of Grid row objects - * @return true, if the set of selected rows was changed. - */ - public boolean deselect(Collection rows); - - } - - /** - * Interface for a selection model that does not allow anything to be - * selected. - * - * @param - * type parameter corresponding with Grid row type - */ - public interface None extends SelectionModel { - - } - -} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelMulti.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelMulti.java deleted file mode 100644 index 3c395560a76..00000000000 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelMulti.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * 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.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.client.renderers.Renderer; -import com.vaadin.client.widgets.Grid; - -/** - * Multi-row selection model. - * - * @author Vaadin Ltd - * @since 7.4 - */ -public class SelectionModelMulti extends AbstractRowHandleSelectionModel - implements SelectionModel.Multi.Batched { - - private final LinkedHashSet> selectedRows; - private Renderer renderer; - private Grid grid; - - private boolean batchStarted = false; - private final LinkedHashSet> selectionBatch = new LinkedHashSet<>(); - private final LinkedHashSet> deselectionBatch = new LinkedHashSet<>(); - - /* Event handling for selection with space key */ - private SpaceSelectHandler spaceSelectHandler; - - public SelectionModelMulti() { - grid = null; - renderer = null; - selectedRows = new LinkedHashSet<>(); - } - - @Override - public boolean isSelected(T row) { - return isSelectedByHandle(grid.getDataSource().getHandle(row)); - } - - @Override - public Renderer getSelectionColumnRenderer() { - return renderer; - } - - @Override - public void setGrid(Grid 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<>(grid); - this.renderer = new MultiSelectionRenderer<>(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> selectedRowsClone = (LinkedHashSet>) selectedRows - .clone(); - SelectionEvent event = new SelectionEvent<>(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 rows) { - if (rows == null) { - throw new IllegalArgumentException("Rows cannot be null"); - } - - Set added = new LinkedHashSet<>(); - - for (T row : rows) { - RowHandle handle = grid.getDataSource().getHandle(row); - if (selectByHandle(handle)) { - added.add(row); - } - } - - if (added.size() > 0) { - grid.fireEvent(new SelectionEvent<>(grid, added, null, - isBeingBatchSelected())); - - return true; - } - return false; - } - - @Override - public boolean deselect(Collection rows) { - if (rows == null) { - throw new IllegalArgumentException("Rows cannot be null"); - } - - Set removed = new LinkedHashSet<>(); - - for (T row : rows) { - RowHandle handle = grid.getDataSource().getHandle(row); - if (deselectByHandle(handle)) { - removed.add(row); - } - } - - if (removed.size() > 0) { - grid.fireEvent(new SelectionEvent<>(grid, null, removed, - isBeingBatchSelected())); - return true; - } - return false; - } - - protected boolean isSelectedByHandle(RowHandle handle) { - return selectedRows.contains(handle); - } - - @Override - protected boolean selectByHandle(RowHandle handle) { - if (selectedRows.add(handle)) { - handle.pin(); - - if (isBeingBatchSelected()) { - deselectionBatch.remove(handle); - selectionBatch.add(handle); - } - - return true; - } - return false; - } - - @Override - protected boolean deselectByHandle(RowHandle handle) { - if (selectedRows.remove(handle)) { - - if (!isBeingBatchSelected()) { - handle.unpin(); - } else { - selectionBatch.remove(handle); - deselectionBatch.add(handle); - } - return true; - } - return false; - } - - @Override - public Collection getSelectedRows() { - Set selected = new LinkedHashSet<>(); - for (RowHandle 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 added = getSelectedRowsBatch(); - selectionBatch.clear(); - - final Collection removed = getDeselectedRowsBatch(); - - // unpin deselected rows - for (RowHandle handle : deselectionBatch) { - handle.unpin(); - } - deselectionBatch.clear(); - - grid.fireEvent(new SelectionEvent<>(grid, added, removed, - isBeingBatchSelected())); - } - - @Override - public boolean isBeingBatchSelected() { - return batchStarted; - } - - @Override - public Collection getSelectedRowsBatch() { - return rowHandlesToRows(selectionBatch); - } - - @Override - public Collection getDeselectedRowsBatch() { - return rowHandlesToRows(deselectionBatch); - } - - private ArrayList rowHandlesToRows(Collection> rowHandles) { - ArrayList rows = new ArrayList<>(rowHandles.size()); - for (RowHandle handle : rowHandles) { - rows.add(handle.getRow()); - } - return rows; - } -} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelNone.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelNone.java deleted file mode 100644 index 3aca4193930..00000000000 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelNone.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.client.widget.grid.selection; - -import java.util.Collection; -import java.util.Collections; - -import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widgets.Grid; - -/** - * No-row selection model. - * - * @author Vaadin Ltd - * @since 7.4 - */ -public class SelectionModelNone extends AbstractRowHandleSelectionModel - implements SelectionModel.None { - - @Override - public boolean isSelected(T row) { - return false; - } - - @Override - public Renderer getSelectionColumnRenderer() { - return null; - } - - @Override - public void setGrid(Grid grid) { - // noop - } - - @Override - public void reset() { - // noop - } - - @Override - public Collection getSelectedRows() { - return Collections.emptySet(); - } - - @Override - protected boolean selectByHandle(RowHandle handle) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "This selection model " + "does not support selection"); - } - - @Override - protected boolean deselectByHandle(RowHandle handle) - throws UnsupportedOperationException { - throw new UnsupportedOperationException( - "This selection model " + "does not support deselection"); - } - -} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java deleted file mode 100644 index 1214e3f72bf..00000000000 --- a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelSingle.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * 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.client.widget.grid.selection; - -import java.util.Collection; -import java.util.Collections; - -import com.vaadin.client.data.DataSource.RowHandle; -import com.vaadin.client.renderers.Renderer; -import com.vaadin.client.widgets.Grid; - -/** - * Single-row selection model. - * - * @author Vaadin Ltd - * @since 7.4 - */ -public class SelectionModelSingle extends AbstractRowHandleSelectionModel - implements SelectionModel.Single { - - private Grid grid; - private RowHandle selectedRow; - - /** Event handling for selection with space key */ - private SpaceSelectHandler spaceSelectHandler; - - /** Event handling for selection by clicking cells */ - private ClickSelectHandler clickSelectHandler; - - private boolean deselectAllowed = true; - - @Override - public boolean isSelected(T row) { - return selectedRow != null - && selectedRow.equals(grid.getDataSource().getHandle(row)); - } - - @Override - public Renderer getSelectionColumnRenderer() { - // No Selection column renderer for single selection - return null; - } - - @Override - public void setGrid(Grid 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<>(grid); - clickSelectHandler = new ClickSelectHandler<>(grid); - updateHandlerDeselectAllowed(); - } else { - spaceSelectHandler.removeHandler(); - clickSelectHandler.removeHandler(); - spaceSelectHandler = null; - clickSelectHandler = null; - } - } - - @Override - public boolean select(T row) { - - if (row == null) { - throw new IllegalArgumentException("Row cannot be null"); - } - - T removed = getSelectedRow(); - if (selectByHandle(grid.getDataSource().getHandle(row))) { - grid.fireEvent(new SelectionEvent<>(grid, row, removed, false)); - - return true; - } - return false; - } - - @Override - public boolean deselect(T row) { - - if (row == null) { - throw new IllegalArgumentException("Row cannot be null"); - } - - if (isSelected(row)) { - deselectByHandle(selectedRow); - grid.fireEvent(new SelectionEvent<>(grid, null, row, false)); - return true; - } - - return false; - } - - @Override - public T getSelectedRow() { - return (selectedRow != null ? selectedRow.getRow() : null); - } - - @Override - public void reset() { - if (selectedRow != null) { - deselect(getSelectedRow()); - } - } - - @Override - public Collection getSelectedRows() { - if (getSelectedRow() != null) { - return Collections.singleton(getSelectedRow()); - } - return Collections.emptySet(); - } - - @Override - protected boolean selectByHandle(RowHandle handle) { - if (handle != null && !handle.equals(selectedRow)) { - deselectByHandle(selectedRow); - selectedRow = handle; - selectedRow.pin(); - return true; - } else { - return false; - } - } - - @Override - protected boolean deselectByHandle(RowHandle handle) { - if (handle != null && handle.equals(selectedRow)) { - selectedRow.unpin(); - selectedRow = null; - return true; - } else { - return false; - } - } - - @Override - public void setDeselectAllowed(boolean deselectAllowed) { - this.deselectAllowed = deselectAllowed; - updateHandlerDeselectAllowed(); - } - - @Override - public boolean isDeselectAllowed() { - return deselectAllowed; - } - - private void updateHandlerDeselectAllowed() { - if (spaceSelectHandler != null) { - spaceSelectHandler.setDeselectAllowed(deselectAllowed); - } - if (clickSelectHandler != null) { - clickSelectHandler.setDeselectAllowed(deselectAllowed); - } - } - -} diff --git a/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelWithSelectionColumn.java b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelWithSelectionColumn.java new file mode 100644 index 00000000000..eeb99916f3c --- /dev/null +++ b/client/src/main/java/com/vaadin/client/widget/grid/selection/SelectionModelWithSelectionColumn.java @@ -0,0 +1,42 @@ +/* + * 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.client.widget.grid.selection; + +import com.vaadin.client.renderers.Renderer; +import com.vaadin.shared.data.selection.SelectionModel; + +/** + * Interface for SelectionModels that wants Grid to display a selection column. + * + * @author Vaadin Ltd + * @since + * + * @param + * selected item type + * + * @see Renderer + */ +public interface SelectionModelWithSelectionColumn + extends SelectionModel { + + /** + * Returns a new instance of the Renderer for selection column. + * + * @return selection column renderer + */ + public Renderer getRenderer(); + +} diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index 92dc0d70432..9a87d82363a 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -159,12 +159,7 @@ import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; import com.vaadin.client.widget.grid.selection.SelectionEvent; import com.vaadin.client.widget.grid.selection.SelectionHandler; -import com.vaadin.client.widget.grid.selection.SelectionModel; -import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; -import com.vaadin.client.widget.grid.selection.SelectionModel.Single; -import com.vaadin.client.widget.grid.selection.SelectionModelMulti; -import com.vaadin.client.widget.grid.selection.SelectionModelNone; -import com.vaadin.client.widget.grid.selection.SelectionModelSingle; +import com.vaadin.client.widget.grid.selection.SelectionModelWithSelectionColumn; import com.vaadin.client.widget.grid.sort.Sort; import com.vaadin.client.widget.grid.sort.SortEvent; import com.vaadin.client.widget.grid.sort.SortHandler; @@ -176,6 +171,8 @@ import com.vaadin.client.widgets.Grid.StaticSection.StaticRow; import com.vaadin.shared.Range; import com.vaadin.shared.Registration; +import com.vaadin.shared.data.selection.SelectionModel; +import com.vaadin.shared.data.selection.SelectionModel.Multi; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.GridConstants.Section; @@ -4445,7 +4442,7 @@ public void onPreviewNativeEvent( private int getSelectionAndFrozenColumnCount() { // no matter if selection column is frozen or not, it is considered // frozen for column dnd reorder - if (getSelectionModel().getSelectionColumnRenderer() != null) { + if (getSelectionModel() instanceof SelectionModelWithSelectionColumn) { return Math.max(0, getFrozenColumnCount()) + 1; } else { return Math.max(0, getFrozenColumnCount()); @@ -4563,47 +4560,6 @@ private void calculatePossibleDropPositions() { }; - /** - * Enumeration for easy setting of selection mode. - */ - public enum SelectionMode { - - /** - * Shortcut for {@link SelectionModelSingle}. - */ - SINGLE { - - @Override - protected SelectionModel createModel() { - return GWT.create(SelectionModelSingle.class); - } - }, - - /** - * Shortcut for {@link SelectionModelMulti}. - */ - MULTI { - - @Override - protected SelectionModel createModel() { - return GWT.create(SelectionModelMulti.class); - } - }, - - /** - * Shortcut for {@link SelectionModelNone}. - */ - NONE { - - @Override - protected SelectionModel createModel() { - return GWT.create(SelectionModelNone.class); - } - }; - - protected abstract SelectionModel createModel(); - } - /** * Base class for grid columns internally used by the Grid. The user should * use {@link Column} when creating new columns. @@ -4776,7 +4732,7 @@ public Column setHeaderCaption(String caption) { } /** - * Returns the current header caption for this column + * Returns the current header caption for this column. * * @since 7.6 * @return the header caption string @@ -4888,6 +4844,7 @@ public Column setRenderer(Renderer renderer) * * @param pixels * the width in pixels or negative for auto sizing + * @return this column */ public Column setWidth(double pixels) { if (!WidgetUtil.pixelValuesEqual(widthUser, pixels)) { @@ -4990,6 +4947,7 @@ public boolean isSortable() { * @param resizable * {@code true} if this column should be resizable, * {@code false} otherwise + * @return this column */ public Column setResizable(boolean resizable) { if (this.resizable != resizable) { @@ -5026,6 +4984,7 @@ public boolean isResizable() { * @param hidden * true to hide the column, false * to show + * @return this column */ public Column setHidden(boolean hidden) { setHidden(hidden, false); @@ -5089,6 +5048,7 @@ public boolean isHidden() { * @param hidable * {@code true} the user can hide this column, {@code false} * otherwise + * @return this column */ public Column setHidable(boolean hidable) { if (this.hidable != hidable) { @@ -5124,6 +5084,7 @@ public boolean isHidable() { * @since 7.5.0 * @param hidingToggleCaption * the caption for the hiding toggle for this column + * @return this column */ public Column setHidingToggleCaption(String hidingToggleCaption) { this.hidingToggleCaption = hidingToggleCaption; @@ -5211,11 +5172,6 @@ public Column setMinimumWidth(double pixels) { * * @param pixels * the maximum width - * @param immediately - * true if the widths should be executed - * immediately (ignoring lazy loading completely), or - * false if the command should be run after a - * while (duplicate non-immediately invocations are ignored). * @return this column */ public Column setMaximumWidth(double pixels) { @@ -5257,7 +5213,7 @@ public Column setMaximumWidth(double pixels) { * returns. This is done to reduce overhead of unintentionally always * recalculate all columns, when modifying several columns at once. * - * @param expandRatio + * @param ratio * the expand ratio of this column. {@code 0} to not have it * expand at all. A negative number to clear the expand * value. @@ -5919,7 +5875,22 @@ public Grid() { editor.setGrid(this); - setSelectionMode(SelectionMode.SINGLE); + setSelectionModel(new SelectionModel() { + + @Override + public Set getSelectedItems() { + return Collections.emptySet(); + } + + @Override + public void select(T item) { + } + + @Override + public void deselect(T item) { + } + + }); escalator.getBody().setSpacerUpdater(gridSpacerUpdater); @@ -6478,7 +6449,7 @@ public void removeHeaderRow(HeaderRow row) { /** * Removes the row at the given position from the header section. * - * @param index + * @param rowIndex * the position of the row * * @throws IllegalArgumentException @@ -6627,7 +6598,7 @@ public void removeFooterRow(FooterRow row) { /** * Removes the row at the given position from the footer section. * - * @param index + * @param rowIndex * the position of the row * * @throws IllegalArgumentException @@ -6700,8 +6671,6 @@ public void setDataSource(final DataSource dataSource) throw new IllegalArgumentException("dataSource can't be null."); } - selectionModel.reset(); - if (changeHandler != null) { changeHandler.remove(); changeHandler = null; @@ -6990,7 +6959,7 @@ public void setScrollTop(double px) { } /** - * Gets the vertical scroll offset + * Gets the vertical scroll offset. * * @return the number of pixels this grid is scrolled down */ @@ -6999,7 +6968,7 @@ public double getScrollTop() { } /** - * Sets the horizontal scroll offset + * Sets the horizontal scroll offset. * * @since 7.5.0 * @param px @@ -7010,7 +6979,7 @@ public void setScrollLeft(double px) { } /** - * Gets the horizontal scroll offset + * Gets the horizontal scroll offset. * * @return the number of pixels this grid is scrolled to the right */ @@ -7622,15 +7591,14 @@ public void setSelectionModel(SelectionModel selectionModel) { throw new IllegalArgumentException("Selection model can't be null"); } - if (this.selectionModel != null) { - // Detach selection model from Grid. - this.selectionModel.setGrid(null); - } - this.selectionModel = selectionModel; - selectionModel.setGrid(this); - setSelectColumnRenderer( - this.selectionModel.getSelectionColumnRenderer()); + if (selectionModel instanceof SelectionModelWithSelectionColumn) { + setSelectColumnRenderer( + ((SelectionModelWithSelectionColumn) selectionModel) + .getRenderer()); + } else { + setSelectColumnRenderer(null); + } // Refresh rendered rows to update selection, if it has changed refreshBody(); @@ -7646,33 +7614,19 @@ public SelectionModel getSelectionModel() { } /** - * Sets current selection mode. - *

- * This is a shorthand method for {@link Grid#setSelectionModel}. - * - * @param mode - * a selection mode value - * @see {@link SelectionMode}. - */ - public void setSelectionMode(SelectionMode mode) { - SelectionModel model = mode.createModel(); - setSelectionModel(model); - } - - /** - * Test if a row is selected. + * Returns if a row is selected. * * @param row * a row object - * @return true, if the current selection model considers the provided row - * object selected. + * @return {@code true}, if the current selection model considers the + * provided row object selected. */ public boolean isSelected(T row) { return selectionModel.isSelected(row); } /** - * Select a row using the current selection model. + * Selects a row using the current selection model. *

* Only selection models implementing {@link SelectionModel.Single} and * {@link SelectionModel.Multi} are supported; for anything else, an @@ -7680,24 +7634,16 @@ public boolean isSelected(T row) { * * @param row * a row object - * @return true iff the current selection changed * @throws IllegalStateException * if the current selection model is not an instance of * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ - public boolean select(T row) { - if (selectionModel instanceof SelectionModel.Single) { - return ((SelectionModel.Single) selectionModel).select(row); - } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel) - .select(Collections.singleton(row)); - } else { - throw new IllegalStateException("Unsupported selection model"); - } + public void select(T row) { + getSelectionModel().select(row); } /** - * Deselect a row using the current selection model. + * Deselects a row using the current selection model. *

* Only selection models implementing {@link SelectionModel.Single} and * {@link SelectionModel.Multi} are supported; for anything else, an @@ -7705,45 +7651,23 @@ public boolean select(T row) { * * @param row * a row object - * @return true iff the current selection changed * @throws IllegalStateException * if the current selection model is not an instance of * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ - public boolean deselect(T row) { - if (selectionModel instanceof SelectionModel.Single) { - return ((SelectionModel.Single) selectionModel).deselect(row); - } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel) - .deselect(Collections.singleton(row)); - } else { - throw new IllegalStateException("Unsupported selection model"); - } + public void deselect(T row) { + getSelectionModel().deselect(row); } /** - * Deselect all rows using the current selection model. + * Deselects all rows using the current selection model. * - * @param row - * a row object - * @return true iff the current selection changed * @throws IllegalStateException * if the current selection model is not an instance of * {@link SelectionModel.Single} or {@link SelectionModel.Multi} */ - public boolean deselectAll() { - if (selectionModel instanceof SelectionModel.Single) { - Single single = ((SelectionModel.Single) selectionModel); - if (single.getSelectedRow() != null) { - return single.deselect(single.getSelectedRow()); - } else { - return false; - } - } else if (selectionModel instanceof SelectionModel.Multi) { - return ((SelectionModel.Multi) selectionModel).deselectAll(); - } else { - throw new IllegalStateException("Unsupported selection model"); - } + public void deselectAll() { + getSelectionModel().deselectAll(); } /** @@ -7759,12 +7683,8 @@ public boolean deselectAll() { * {@link SelectionModel.Single} */ public T getSelectedRow() { - if (selectionModel instanceof SelectionModel.Single) { - return ((SelectionModel.Single) selectionModel).getSelectedRow(); - } else { - throw new IllegalStateException( - "Unsupported selection model; can not get single selected row"); - } + return getSelectionModel().getSelectedItems().stream().findFirst() + .orElse(null); } /** @@ -7773,7 +7693,7 @@ public T getSelectedRow() { * @return a non-null collection containing all currently selected rows. */ public Collection getSelectedRows() { - return selectionModel.getSelectedRows(); + return getSelectionModel().getSelectedItems(); } @Override @@ -7884,6 +7804,7 @@ public HandlerRegistration addSortHandler(SortHandler handler) { * * @param handler * a select all event handler + * @return the registration for the event */ public HandlerRegistration addSelectAllHandler( SelectAllHandler handler) { @@ -8318,7 +8239,7 @@ public void setColumnOrder(Column... orderedColumns) { } /** - * Sets the style generator that is used for generating styles for cells + * Sets the style generator that is used for generating styles for cells. * * @param cellStyleGenerator * the cell style generator to set, or null to @@ -8331,7 +8252,7 @@ public void setCellStyleGenerator( } /** - * Gets the style generator that is used for generating styles for cells + * Gets the style generator that is used for generating styles for cells. * * @return the cell style generator, or null if no generator is * set @@ -8341,7 +8262,7 @@ public CellStyleGenerator getCellStyleGenerator() { } /** - * Sets the style generator that is used for generating styles for rows + * Sets the style generator that is used for generating styles for rows. * * @param rowStyleGenerator * the row style generator to set, or null to remove @@ -8353,7 +8274,7 @@ public void setRowStyleGenerator(RowStyleGenerator rowStyleGenerator) { } /** - * Gets the style generator that is used for generating styles for rows + * Gets the style generator that is used for generating styles for rows. * * @return the row style generator, or null if no generator is * set @@ -8443,7 +8364,7 @@ public EditorHandler getEditorHandler() { * Sets the handler responsible for binding data and editor widgets to the * editor. * - * @param rowHandler + * @param handler * the new editor handler * * @throws IllegalStateException @@ -8802,9 +8723,7 @@ public void setDetailsVisible(int rowIndex, boolean visible) { if (visible && !isVisible) { escalator.getBody().setSpacer(rowIndex, DETAILS_ROW_INITIAL_HEIGHT); visibleDetails.add(rowIndexInteger); - } - - else if (!visible && isVisible) { + } else if (!visible && isVisible) { escalator.getBody().setSpacer(rowIndex, -1); visibleDetails.remove(rowIndexInteger); } @@ -8911,7 +8830,7 @@ public void focus() { * Sets the buffered editor mode. * * @since 7.6 - * @param editorUnbuffered + * @param editorBuffered * true to enable buffered editor, * false to disable it */ diff --git a/server/src/main/java/com/vaadin/data/selection/SingleSelection.java b/server/src/main/java/com/vaadin/data/selection/SingleSelection.java index fb6d3b1cacf..54f0240b373 100644 --- a/server/src/main/java/com/vaadin/data/selection/SingleSelection.java +++ b/server/src/main/java/com/vaadin/data/selection/SingleSelection.java @@ -88,8 +88,8 @@ public Optional getSelectedItem() { * @see SingleSelectionChange */ @FunctionalInterface - public interface SingleSelectionListener extends - EventListener> { + public interface SingleSelectionListener + extends EventListener> { @Override public void accept(SingleSelectionChange event); @@ -112,12 +112,14 @@ public SingleSelection( @Override public void select(String key) { - doSelect(getData(key), true); + if (!Objects.equals(selectedItem, getData(key))) { + doSelect(getData(key), true); + } } @Override public void deselect(String key) { - if (getData(key).equals(selectedItem)) { + if (Objects.equals(selectedItem, getData(key))) { doSelect(null, true); } } @@ -133,14 +135,15 @@ public Optional getSelectedItem() { } @Override - public void select(T value) { - doSelect(value, false); + public void select(T item) { + doSelect(item, false); } @Override public void deselect(T value) { - if(Objects.equals(selectedItem,value)) + if (Objects.equals(selectedItem, value)) { doSelect(null, false); + } } @Override @@ -172,16 +175,22 @@ public Registration addSelectionListener( * Selects the given item or deselects the current one if given * {@code null}. * - * @param value + * @param item * the item to select or {@code null} to deselect * @param userOriginated * {@code true} if this event originates from the client, * {@code false} otherwise. */ - protected void doSelect(T value, boolean userOriginated) { - if (!Objects.equals(value, this.selectedItem)) { - this.selectedItem = value; - fireEvent(new SingleSelectionChange<>(getParent(), value, + protected void doSelect(T item, boolean userOriginated) { + if (!Objects.equals(item, selectedItem)) { + if (selectedItem != null) { + refresh(selectedItem); + } + selectedItem = item; + if (selectedItem != null) { + refresh(selectedItem); + } + fireEvent(new SingleSelectionChange<>(getParent(), selectedItem, userOriginated)); } } diff --git a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java index 8c92ad50e31..5aceb9b3051 100644 --- a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java +++ b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java @@ -373,6 +373,11 @@ protected void reset() { * updated data object */ public void refresh(T data) { + if (!handler.getActiveData().contains(data)) { + // Item is not currently available at the client-side + return; + } + if (updatedData.isEmpty()) { markAsDirty(); } diff --git a/server/src/main/java/com/vaadin/ui/AbstractListing.java b/server/src/main/java/com/vaadin/ui/AbstractListing.java index 28549034639..5996b52c9b4 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractListing.java +++ b/server/src/main/java/com/vaadin/ui/AbstractListing.java @@ -160,6 +160,11 @@ public SELECTIONMODEL getSelectionModel() { * the selection model to use, not null */ protected void setSelectionModel(SELECTIONMODEL model) { + if (selectionModel != null) { + throw new IllegalStateException( + "A selection model can't be changed."); + } + Objects.requireNonNull(model, "selection model cannot be null"); selectionModel = model; } diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index ae7c5501726..387cd222757 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -34,9 +34,9 @@ import com.vaadin.data.selection.SingleSelection; import com.vaadin.server.AbstractExtension; import com.vaadin.server.KeyMapper; +import com.vaadin.server.data.DataGenerator; import com.vaadin.server.data.DataSource; import com.vaadin.server.data.SortOrder; -import com.vaadin.server.data.DataGenerator; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.selection.SelectionModel; diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java new file mode 100644 index 00000000000..0522e26ac98 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridSelectionTest.java @@ -0,0 +1,34 @@ +package com.vaadin.tests.components.grid; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.ui.Grid; + +public class GridSelectionTest { + + Grid grid; + + @Before + public void setUp() { + grid = new Grid<>(); + grid.setItems("Foo", "Bar"); + } + + @Test + public void testGridWithSingleSelection() { + Assert.assertFalse(grid.isSelected("Foo")); + grid.select("Foo"); + Assert.assertTrue(grid.isSelected("Foo")); + Assert.assertEquals(1, grid.getSelectedItems().size()); + Assert.assertEquals("Foo", grid.getSelectedItems().iterator().next()); + grid.select("Bar"); + Assert.assertFalse(grid.isSelected("Foo")); + Assert.assertTrue(grid.isSelected("Bar")); + grid.deselect("Bar"); + Assert.assertFalse(grid.isSelected("Bar")); + Assert.assertEquals(0, grid.getSelectedItems().size()); + } + +} diff --git a/shared/src/main/java/com/vaadin/shared/data/selection/SelectionModel.java b/shared/src/main/java/com/vaadin/shared/data/selection/SelectionModel.java index 8fff6ee434b..270b964a791 100644 --- a/shared/src/main/java/com/vaadin/shared/data/selection/SelectionModel.java +++ b/shared/src/main/java/com/vaadin/shared/data/selection/SelectionModel.java @@ -1,12 +1,12 @@ /* * 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 @@ -23,9 +23,9 @@ /** * Models the selection logic of a {@code Listing} component. Determines how * items can be selected and deselected. - * + * * @author Vaadin Ltd. - * + * * @param * the type of the items to select * @since @@ -51,7 +51,7 @@ public interface Single extends SelectionModel { /** * Returns the currently selected item, or an empty optional if no item * is selected. - * + * * @return an optional of the selected item if any, an empty optional * otherwise */ @@ -60,7 +60,7 @@ public interface Single extends SelectionModel { /** * Returns a singleton set of the currently selected item or an empty * set if no item is selected. - * + * * @return a singleton set of the selected item if any, an empty set * otherwise */ @@ -85,6 +85,7 @@ public interface Multi extends SelectionModel { */ @Override public void select(T item); + } /** @@ -93,7 +94,7 @@ public interface Multi extends SelectionModel { * Implementation note: the iteration order of the items in the * returned set should be well-defined and documented by the implementing * class. - * + * * @return the items in the current selection, not null */ public Set getSelectedItems(); @@ -101,7 +102,7 @@ public interface Multi extends SelectionModel { /** * Selects the given item. Depending on the implementation, may cause other * items to be deselected. If the item is already selected, does nothing. - * + * * @param item * the item to select, not null */ @@ -110,15 +111,22 @@ public interface Multi extends SelectionModel { /** * Deselects the given item. If the item is not currently selected, does * nothing. - * + * * @param item * the item to deselect, not null */ public void deselect(T item); + /** + * Deselects all currently selected items. + */ + public default void deselectAll() { + getSelectedItems().forEach(this::deselect); + } + /** * Returns whether the given item is currently selected. - * + * * @param item * the item to check, not null * @return {@code true} if the item is selected, {@code false} otherwise diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java index ebcdc34d7f2..ff272689915 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java @@ -64,6 +64,11 @@ public void setDate(Date date) { this.date = date; } + @Override + public String toString() { + return "DataObject[" + rowNumber + "]"; + } + static List generateObjects() { List data = new ArrayList<>(); diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java index 3445a3e3f31..b78e5272fec 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java @@ -10,6 +10,7 @@ import java.util.stream.Stream; import com.vaadin.annotations.Widgetset; +import com.vaadin.data.selection.SingleSelection; import com.vaadin.server.VaadinRequest; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.tests.components.AbstractTestUIWithLog; @@ -112,6 +113,10 @@ protected void setup(VaadinRequest request) { grid = new Grid<>(); grid.setItems(data); + grid.addColumn("Column 0", String.class, + dataObj -> "(" + dataObj.getRowNumber() + ", 0)"); + grid.addColumn("Column 1", String.class, + dataObj -> "(" + dataObj.getRowNumber() + ", 1)"); grid.addColumn("Row Number", Integer.class, DataObject::getRowNumber); grid.addColumn("Date", Date.class, DataObject::getDate); grid.addColumn("HTML String", String.class, DataObject::getHtmlString); @@ -119,6 +124,9 @@ protected void setup(VaadinRequest request) { grid.addColumn("Small Random", Integer.class, DataObject::getSmallRandom); + ((SingleSelection) grid.getSelectionModel()) + .addSelectionListener(e -> log("Selected: " + e.getValue())); + layout.addComponent(createMenu()); layout.addComponent(grid); addComponent(layout); @@ -130,6 +138,7 @@ private Component createMenu() { createStateMenu(componentMenu.addItem("State", null)); createSizeMenu(componentMenu.addItem("Size", null)); createDetailsMenu(componentMenu.addItem("Details", null)); + createBodyMenu(componentMenu.addItem("Body rows", null)); return menu; } @@ -163,6 +172,17 @@ private void addGridMethodMenu(MenuItem parent, String name, T value, parent.addItem(name, menuItem -> method.accept(value)); } + private void createBodyMenu(MenuItem rowMenu) { + rowMenu.addItem("Toggle first row selection", menuItem -> { + DataObject item = data.get(0); + if (grid.isSelected(item)) { + grid.deselect(item); + } else { + grid.select(item); + } + }); + } + /* DetailsGenerator related things */ private void createDetailsMenu(MenuItem detailsMenu) { diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java new file mode 100644 index 00000000000..2c329ca01c5 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicSelectionTest.java @@ -0,0 +1,84 @@ +package com.vaadin.tests.components.grid.basics; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.customelements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridRowElement; + +public class GridBasicSelectionTest extends GridBasicsTest { + + @Test + public void testKeyboardWithSingleSelection() { + + GridElement grid = getGridElement(); + grid.getCell(3, 1).click(); + + assertTrue("Grid row 3 was not selected with clicking.", + grid.getRow(3).isSelected()); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertTrue("Grid row 3 was not deselected with space key.", + !grid.getRow(3).isSelected()); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertTrue("Grid row 3 was not selected with space key.", + grid.getRow(3).isSelected()); + + grid.scrollToRow(500); + + new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); + + assertTrue("Grid row 3 was not deselected with space key.", + !grid.getRow(3).isSelected()); + } + + @Test + public void testSingleSelectionUpdatesFromServer() { + GridElement grid = getGridElement(); + assertFalse("First row was selected from start", + grid.getRow(0).isSelected()); + toggleFirstRowSelection(); + assertTrue("First row was not selected.", getRow(0).isSelected()); + assertTrue("Selection event was not correct", + logContainsText("Selected: DataObject[0]")); + grid.getCell(5, 0).click(); + assertTrue("Fifth row was not selected.", getRow(5).isSelected()); + assertFalse("First row was still selected.", getRow(0).isSelected()); + assertTrue("Selection event was not correct", + logContainsText("Selected: DataObject[5]")); + grid.getCell(0, 6).click(); + assertTrue("Selection event was not correct", + logContainsText("Selected: DataObject[0]")); + toggleFirstRowSelection(); + assertTrue("Selection event was not correct", + logContainsText("Selected: null")); + assertFalse("First row was still selected.", getRow(0).isSelected()); + assertFalse("Fifth row was still selected.", getRow(5).isSelected()); + + grid.scrollToRow(600); + grid.getCell(595, 3).click(); + assertTrue("Row 595 was not selected.", getRow(595).isSelected()); + assertTrue("Selection event was not correct", + logContainsText("Selected: DataObject[595]")); + toggleFirstRowSelection(); + assertFalse("Row 595 was still selected.", getRow(595).isSelected()); + assertTrue("First row was not selected.", getRow(0).isSelected()); + assertTrue("Selection event was not correct", + logContainsText("Selected: DataObject[0]")); + } + + private void toggleFirstRowSelection() { + selectMenuPath("Component", "Body rows", "Toggle first row selection"); + } + + private GridRowElement getRow(int i) { + return getGridElement().getRow(i); + } +}