Change-Id: I62c5a2486360fe11de8a90efabb7775ef47124cbtags/7.4.0.alpha2
@@ -17,6 +17,7 @@ | |||
package com.vaadin.ui.components.grid; | |||
import java.io.Serializable; | |||
import java.lang.reflect.Method; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
@@ -51,7 +52,14 @@ import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.ui.grid.ScrollDestination; | |||
import com.vaadin.ui.AbstractComponent; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.components.grid.selection.MultiSelectionModel; | |||
import com.vaadin.ui.components.grid.selection.NoSelectionModel; | |||
import com.vaadin.ui.components.grid.selection.SelectionChangeEvent; | |||
import com.vaadin.ui.components.grid.selection.SelectionChangeListener; | |||
import com.vaadin.ui.components.grid.selection.SelectionChangeNotifier; | |||
import com.vaadin.ui.components.grid.selection.SelectionModel; | |||
import com.vaadin.ui.components.grid.selection.SingleSelectionModel; | |||
import com.vaadin.util.ReflectTools; | |||
/** | |||
* Data grid component | |||
@@ -71,7 +79,7 @@ import com.vaadin.ui.Component; | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class Grid extends AbstractComponent { | |||
public class Grid extends AbstractComponent implements SelectionChangeNotifier { | |||
/** | |||
* A helper class that handles the client-side Escalator logic relating to | |||
@@ -349,6 +357,47 @@ public class Grid extends AbstractComponent { | |||
} | |||
} | |||
/** | |||
* Selection modes representing built-in {@link SelectionModel | |||
* SelectionModels} that come bundled with {@link Grid}. | |||
* <p> | |||
* Passing one of these enums into | |||
* {@link Grid#setSelectionMode(SelectionMode)} is equivalent to calling | |||
* {@link Grid#setSelectionModel(SelectionModel)} with one of the built-in | |||
* implementations of {@link SelectionModel}. | |||
* | |||
* @see Grid#setSelectionMode(SelectionMode) | |||
* @see Grid#setSelectionModel(SelectionModel) | |||
*/ | |||
public enum SelectionMode { | |||
/** A SelectionMode that maps to {@link SingleSelectionModel} */ | |||
SINGLE { | |||
@Override | |||
protected SelectionModel createModel() { | |||
return new SingleSelectionModel(); | |||
} | |||
}, | |||
/** A SelectionMode that maps to {@link MultiSelectionModel} */ | |||
MULTI { | |||
@Override | |||
protected SelectionModel createModel() { | |||
return new MultiSelectionModel(); | |||
} | |||
}, | |||
/** A SelectionMode that maps to {@link NoSelectionModel} */ | |||
NONE { | |||
@Override | |||
protected SelectionModel createModel() { | |||
return new NoSelectionModel(); | |||
} | |||
}; | |||
protected abstract SelectionModel createModel(); | |||
} | |||
/** | |||
* The data source attached to the grid | |||
*/ | |||
@@ -458,6 +507,16 @@ public class Grid extends AbstractComponent { | |||
private final ActiveRowHandler activeRowHandler = new ActiveRowHandler(); | |||
/** | |||
* The selection model that is currently in use. Never <code>null</code> | |||
* after the constructor has been run. | |||
*/ | |||
private SelectionModel selectionModel; | |||
private static final Method SELECTION_CHANGE_METHOD = ReflectTools | |||
.findMethod(SelectionChangeListener.class, "selectionChange", | |||
SelectionChangeEvent.class); | |||
/** | |||
* Creates a new Grid using the given datasource. | |||
* | |||
@@ -465,7 +524,8 @@ public class Grid extends AbstractComponent { | |||
* the data source for the grid | |||
*/ | |||
public Grid(Container.Indexed datasource) { | |||
setContainerDatasource(datasource); | |||
setContainerDataSource(datasource); | |||
setSelectionMode(SelectionMode.MULTI); | |||
registerRpc(new GridServerRpc() { | |||
@Override | |||
@@ -484,7 +544,7 @@ public class Grid extends AbstractComponent { | |||
* @throws IllegalArgumentException | |||
* if the data source is null | |||
*/ | |||
public void setContainerDatasource(Container.Indexed container) { | |||
public void setContainerDataSource(Container.Indexed container) { | |||
if (container == null) { | |||
throw new IllegalArgumentException( | |||
"Cannot set the datasource to null"); | |||
@@ -512,6 +572,14 @@ public class Grid extends AbstractComponent { | |||
datasourceExtension = new RpcDataProviderExtension(container); | |||
datasourceExtension.extend(this); | |||
/* | |||
* selectionModel == null when the invocation comes from the | |||
* constructor. | |||
*/ | |||
if (selectionModel != null) { | |||
selectionModel.reset(); | |||
} | |||
// Listen to changes in properties and remove columns if needed | |||
if (datasource instanceof PropertySetChangeNotifier) { | |||
((PropertySetChangeNotifier) datasource) | |||
@@ -958,4 +1026,254 @@ public class Grid extends AbstractComponent { | |||
public HeightMode getHeightMode() { | |||
return getState(false).heightMode; | |||
} | |||
/* Selection related methods: */ | |||
/** | |||
* Takes a new {@link SelectionModel} into use. | |||
* <p> | |||
* The SelectionModel that is previously in use will have all its items | |||
* deselected. | |||
* <p> | |||
* If the given SelectionModel is already in use, this method does nothing. | |||
* | |||
* @param selectionModel | |||
* the new SelectionModel to use | |||
* @throws IllegalArgumentException | |||
* if {@code selectionModel} is <code>null</code> | |||
*/ | |||
public void setSelectionModel(SelectionModel selectionModel) | |||
throws IllegalArgumentException { | |||
if (selectionModel == null) { | |||
throw new IllegalArgumentException( | |||
"Selection model may not be null"); | |||
} | |||
if (this.selectionModel != selectionModel) { | |||
// this.selectionModel is null on init | |||
if (this.selectionModel != null) { | |||
this.selectionModel.reset(); | |||
this.selectionModel.setGrid(null); | |||
} | |||
this.selectionModel = selectionModel; | |||
this.selectionModel.setGrid(this); | |||
this.selectionModel.reset(); | |||
} | |||
} | |||
/** | |||
* Returns the currently used {@link SelectionModel}. | |||
* | |||
* @return the currently used SelectionModel | |||
*/ | |||
public SelectionModel getSelectionModel() { | |||
return selectionModel; | |||
} | |||
/** | |||
* Changes the Grid's selection mode. | |||
* <p> | |||
* Grid supports three selection modes: multiselect, single select and no | |||
* selection, and this is a conveniency method for choosing between one of | |||
* them. | |||
* <P> | |||
* Technically, this method is a shortcut that can be used instead of | |||
* calling {@code setSelectionModel} with a specific SelectionModel | |||
* instance. Grid comes with three built-in SelectionModel classes, and the | |||
* {@link SelectionMode} enum represents each of them. | |||
* <p> | |||
* Essentially, the two following method calls are equivalent: | |||
* <p> | |||
* <code><pre> | |||
* grid.setSelectionMode(SelectionMode.MULTI); | |||
* grid.setSelectionModel(new MultiSelectionMode()); | |||
* </pre></code> | |||
* | |||
* | |||
* @param selectionMode | |||
* the selection mode to switch to | |||
* @return The {@link SelectionModel} instance that was taken into use | |||
* @throws IllegalArgumentException | |||
* if {@code selectionMode} is <code>null</code> | |||
* @see SelectionModel | |||
*/ | |||
public SelectionModel setSelectionMode(final SelectionMode selectionMode) | |||
throws IllegalArgumentException { | |||
if (selectionMode == null) { | |||
throw new IllegalArgumentException("selection mode may not be null"); | |||
} | |||
final SelectionModel newSelectionModel = selectionMode.createModel(); | |||
setSelectionModel(newSelectionModel); | |||
return newSelectionModel; | |||
} | |||
/** | |||
* Checks whether an item is selected or not. | |||
* | |||
* @param itemId | |||
* the item id to check for | |||
* @return <code>true</code> iff the item is selected | |||
*/ | |||
// keep this javadoc in sync with SelectionModel.isSelected | |||
public boolean isSelected(Object itemId) { | |||
return selectionModel.isSelected(itemId); | |||
} | |||
/** | |||
* Returns a collection of all the currently selected itemIds. | |||
* <p> | |||
* This method is a shorthand that is forwarded to the object that is | |||
* returned by {@link #getSelectionModel()}. | |||
* | |||
* @return a collection of all the currently selected itemIds | |||
*/ | |||
// keep this javadoc in sync with SelectionModel.getSelectedRows | |||
public Collection<Object> getSelectedRows() { | |||
return getSelectionModel().getSelectedRows(); | |||
} | |||
/** | |||
* Gets the item id of the currently selected item. | |||
* <p> | |||
* This method is a shorthand that is forwarded to the object that is | |||
* returned by {@link #getSelectionModel()}. Only | |||
* {@link SelectionModel.Single} is supported. | |||
* | |||
* @return the item id of the currently selected item, or <code>null</code> | |||
* if nothing is selected | |||
* @throws IllegalStateException | |||
* if the object that is returned by | |||
* {@link #getSelectionModel()} is not an instance of | |||
* {@link SelectionModel.Single} | |||
*/ | |||
// keep this javadoc in sync with SelectionModel.Single.getSelectedRow | |||
public Object getSelectedRow() throws IllegalStateException { | |||
if (selectionModel instanceof SelectionModel.Single) { | |||
return ((SelectionModel.Single) selectionModel).getSelectedRow(); | |||
} else { | |||
throw new IllegalStateException(Grid.class.getSimpleName() | |||
+ " does not support the 'getSelectedRow' shortcut method " | |||
+ "unless the selection model implements " | |||
+ SelectionModel.Single.class.getName() | |||
+ ". The current one does not (" | |||
+ selectionModel.getClass().getName() + ")"); | |||
} | |||
} | |||
/** | |||
* Marks an item as selected. | |||
* <p> | |||
* This method is a shorthand that is forwarded to the object that is | |||
* returned by {@link #getSelectionModel()}. Only | |||
* {@link SelectionModel.Single} or {@link SelectionModel.Multi} are | |||
* supported. | |||
* | |||
* | |||
* @param itemIds | |||
* the itemId to mark as selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if the itemId already was selected | |||
* @throws IllegalArgumentException | |||
* if the {@code itemId} doesn't exist in the currently active | |||
* Container | |||
* @throws IllegalStateException | |||
* if the selection was illegal. One such reason might be that | |||
* the implementation already had an item selected, and that | |||
* needs to be explicitly deselected before re-selecting | |||
* something | |||
* @throws IllegalStateException | |||
* if the object that is returned by | |||
* {@link #getSelectionModel()} does not implement | |||
* {@link SelectionModel.Single} or {@link SelectionModel.Multi} | |||
*/ | |||
// keep this javadoc in sync with SelectionModel.Single.select | |||
public boolean select(Object itemId) throws IllegalArgumentException, | |||
IllegalStateException { | |||
if (selectionModel instanceof SelectionModel.Single) { | |||
return ((SelectionModel.Single) selectionModel).select(itemId); | |||
} else if (selectionModel instanceof SelectionModel.Multi) { | |||
return ((SelectionModel.Multi) selectionModel).select(itemId); | |||
} else { | |||
throw new IllegalStateException(Grid.class.getSimpleName() | |||
+ " does not support the 'select' shortcut method " | |||
+ "unless the selection model implements " | |||
+ SelectionModel.Single.class.getName() + " or " | |||
+ SelectionModel.Multi.class.getName() | |||
+ ". The current one does not (" | |||
+ selectionModel.getClass().getName() + ")."); | |||
} | |||
} | |||
/** | |||
* Marks an item as deselected. | |||
* <p> | |||
* This method is a shorthand that is forwarded to the object that is | |||
* returned by {@link #getSelectionModel()}. Only | |||
* {@link SelectionModel.Single} and {@link SelectionModel.Multi} are | |||
* supported. | |||
* | |||
* @param itemId | |||
* the itemId to remove from being selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if the itemId already was selected | |||
* @throws IllegalArgumentException | |||
* if the {@code itemId} doesn't exist in the currently active | |||
* Container | |||
* @throws IllegalStateException | |||
* if the deselection was illegal. One such reason might be that | |||
* the implementation already had an item selected, and that | |||
* needs to be explicitly deselected before re-selecting | |||
* something | |||
* @throws IllegalStateException | |||
* if the object that is returned by | |||
* {@link #getSelectionModel()} does not implement | |||
* {@link SelectionModel.Single} or {@link SelectionModel.Multi} | |||
*/ | |||
// keep this javadoc in sync with SelectionModel.Single.deselect | |||
public boolean deselect(Object itemId) throws IllegalStateException { | |||
if (selectionModel instanceof SelectionModel.Single) { | |||
return ((SelectionModel.Single) selectionModel).deselect(itemId); | |||
} else if (selectionModel instanceof SelectionModel.Multi) { | |||
return ((SelectionModel.Multi) selectionModel).deselect(itemId); | |||
} else { | |||
throw new IllegalStateException(Grid.class.getSimpleName() | |||
+ " does not support the 'deselect' shortcut method " | |||
+ "unless the selection model implements " | |||
+ SelectionModel.Single.class.getName() + " or " | |||
+ SelectionModel.Multi.class.getName() | |||
+ ". The current one does not (" | |||
+ selectionModel.getClass().getName() + ")."); | |||
} | |||
} | |||
/** | |||
* Fires a selection change event. | |||
* <p> | |||
* <strong>Note:</strong> This is not a method that should be called by | |||
* application logic. This method is publicly accessible only so that | |||
* {@link SelectionModel SelectionModels} would be able to inform Grid of | |||
* these events. | |||
* | |||
* @param addedSelections | |||
* the selections that were added by this event | |||
* @param removedSelections | |||
* the selections that were removed by this event | |||
*/ | |||
public void fireSelectionChangeEvent(Collection<Object> oldSelection, | |||
Collection<Object> newSelection) { | |||
fireEvent(new SelectionChangeEvent(this, oldSelection, newSelection)); | |||
} | |||
@Override | |||
public void addSelectionChangeListener(SelectionChangeListener listener) { | |||
addListener(SelectionChangeEvent.class, listener, | |||
SELECTION_CHANGE_METHOD); | |||
} | |||
@Override | |||
public void removeSelectionChangeListener(SelectionChangeListener listener) { | |||
removeListener(SelectionChangeEvent.class, listener, | |||
SELECTION_CHANGE_METHOD); | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.LinkedHashSet; | |||
import com.vaadin.ui.components.grid.Grid; | |||
/** | |||
* A base class for SelectionModels that contains some of the logic that is | |||
* reusable. | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public abstract class AbstractSelectionModel implements SelectionModel { | |||
protected final LinkedHashSet<Object> selection = new LinkedHashSet<Object>(); | |||
protected Grid grid = null; | |||
@Override | |||
public boolean isSelected(final Object itemId) { | |||
return selection.contains(itemId); | |||
} | |||
@Override | |||
public Collection<Object> getSelectedRows() { | |||
return new ArrayList<Object>(selection); | |||
} | |||
@Override | |||
public void setGrid(final Grid grid) { | |||
this.grid = grid; | |||
} | |||
/** | |||
* Fires a {@link SelectionChangeEvent} to all the | |||
* {@link SelectionChangeListener SelectionChangeListeners} currently added | |||
* to the Grid in which this SelectionModel is. | |||
* <p> | |||
* Note that this is only a helper method, and routes the call all the way | |||
* to Grid. A {@link SelectionModel} is not a | |||
* {@link SelectionChangeNotifier} | |||
* | |||
* @param oldSelection | |||
* the complete {@link Collection} of the itemIds that were | |||
* selected <em>before</em> this event happened | |||
* @param newSelection | |||
* the complete {@link Collection} of the itemIds that are | |||
* selected <em>after</em> this event happened | |||
*/ | |||
protected void fireSelectionChangeEvent( | |||
final Collection<Object> oldSelection, | |||
final Collection<Object> newSelection) { | |||
grid.fireSelectionChangeEvent(oldSelection, newSelection); | |||
} | |||
} |
@@ -0,0 +1,138 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import com.vaadin.data.Container.Indexed; | |||
/** | |||
* A default implementation of a {@link SelectionModel.Multi} | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class MultiSelectionModel extends AbstractSelectionModel implements | |||
SelectionModel.Multi { | |||
@Override | |||
public boolean select(final Object... itemIds) | |||
throws IllegalArgumentException { | |||
if (itemIds != null) { | |||
// select will fire the event | |||
return select(Arrays.asList(itemIds)); | |||
} else { | |||
throw new IllegalArgumentException( | |||
"Vararg array of itemIds may not be null"); | |||
} | |||
} | |||
@Override | |||
public boolean select(final Collection<?> itemIds) | |||
throws IllegalArgumentException { | |||
if (itemIds == null) { | |||
throw new IllegalArgumentException("itemIds may not be null"); | |||
} | |||
final boolean hasSomeDifferingElements = !selection | |||
.containsAll(itemIds); | |||
if (hasSomeDifferingElements) { | |||
final HashSet<Object> oldSelection = new HashSet<Object>(selection); | |||
selection.addAll(itemIds); | |||
fireSelectionChangeEvent(oldSelection, selection); | |||
} | |||
return hasSomeDifferingElements; | |||
} | |||
@Override | |||
public boolean deselect(final Object... itemIds) | |||
throws IllegalArgumentException { | |||
if (itemIds != null) { | |||
// deselect will fire the event | |||
return deselect(Arrays.asList(itemIds)); | |||
} else { | |||
throw new IllegalArgumentException( | |||
"Vararg array of itemIds may not be null"); | |||
} | |||
} | |||
@Override | |||
public boolean deselect(final Collection<?> itemIds) | |||
throws IllegalArgumentException { | |||
if (itemIds == null) { | |||
throw new IllegalArgumentException("itemIds may not be null"); | |||
} | |||
final boolean hasCommonElements = !Collections.disjoint(itemIds, | |||
selection); | |||
if (hasCommonElements) { | |||
final HashSet<Object> oldSelection = new HashSet<Object>(selection); | |||
selection.removeAll(itemIds); | |||
fireSelectionChangeEvent(oldSelection, selection); | |||
} | |||
return hasCommonElements; | |||
} | |||
@Override | |||
public boolean selectAll() { | |||
// select will fire the event | |||
final Indexed container = grid.getContainerDatasource(); | |||
if (container != null) { | |||
return select(container.getItemIds()); | |||
} else if (selection.isEmpty()) { | |||
return false; | |||
} else { | |||
/* | |||
* this should never happen (no container but has a selection), but | |||
* I guess the only theoretically correct course of action... | |||
*/ | |||
return deselectAll(); | |||
} | |||
} | |||
@Override | |||
public boolean deselectAll() { | |||
// deselect will fire the event | |||
return deselect(getSelectedRows()); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* <p> | |||
* The returned Collection is in <strong>order of selection</strong> – | |||
* the item that was first selected will be first in the collection, and so | |||
* on. Should an item have been selected twice without being deselected in | |||
* between, it will have remained in its original position. | |||
*/ | |||
@Override | |||
public Collection<Object> getSelectedRows() { | |||
// overridden only for JavaDoc | |||
return super.getSelectedRows(); | |||
} | |||
/** | |||
* Resets the selection model. | |||
* <p> | |||
* Equivalent to calling {@link #deselectAll()} | |||
*/ | |||
@Override | |||
public void reset() { | |||
deselectAll(); | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import com.vaadin.ui.components.grid.Grid; | |||
/** | |||
* A default implementation for a {@link SelectionModel.None} | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class NoSelectionModel implements SelectionModel.None { | |||
@Override | |||
public void setGrid(final Grid grid) { | |||
// NOOP, not needed for anything | |||
} | |||
@Override | |||
public boolean isSelected(final Object itemId) { | |||
return false; | |||
} | |||
@Override | |||
public Collection<Object> getSelectedRows() { | |||
return Collections.emptyList(); | |||
} | |||
/** | |||
* Semantically resets the selection model. | |||
* <p> | |||
* Effectively a no-op. | |||
*/ | |||
@Override | |||
public void reset() { | |||
// NOOP | |||
} | |||
} |
@@ -0,0 +1,73 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.util.Collection; | |||
import java.util.EventObject; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import com.google.gwt.thirdparty.guava.common.collect.Sets; | |||
import com.vaadin.ui.components.grid.Grid; | |||
/** | |||
* An event that specifies what in a selection has changed, and where the | |||
* selection took place. | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class SelectionChangeEvent extends EventObject { | |||
private Set<Object> oldSelection; | |||
private Set<Object> newSelection; | |||
public SelectionChangeEvent(Grid source, Collection<Object> oldSelection, | |||
Collection<Object> newSelection) { | |||
super(source); | |||
this.oldSelection = new HashSet<Object>(oldSelection); | |||
this.newSelection = new HashSet<Object>(newSelection); | |||
} | |||
/** | |||
* A {@link Collection} of all the itemIds that became selected. | |||
* <p> | |||
* <em>Note:</em> this excludes all itemIds that might have been previously | |||
* selected. | |||
* | |||
* @return a Collection of the itemIds that became selected | |||
*/ | |||
public Set<Object> getAdded() { | |||
return Sets.difference(newSelection, oldSelection); | |||
} | |||
/** | |||
* A {@link Collection} of all the itemIds that became deselected. | |||
* <p> | |||
* <em>Note:</em> this excludes all itemIds that might have been previously | |||
* deselected. | |||
* | |||
* @return a Collection of the itemIds that became deselected | |||
*/ | |||
public Set<Object> getRemoved() { | |||
return Sets.difference(oldSelection, newSelection); | |||
} | |||
@Override | |||
public Grid getSource() { | |||
return (Grid) super.getSource(); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.io.Serializable; | |||
/** | |||
* The listener interface for receiving {@link SelectionChangeEvent | |||
* SelectionChangeEvents}. | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface SelectionChangeListener extends Serializable { | |||
/** | |||
* Notifies the listener that the selection state has changed. | |||
* | |||
* @param event | |||
* the selection change event | |||
*/ | |||
void selectionChange(SelectionChangeEvent event); | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.io.Serializable; | |||
/** | |||
* The interface for adding and removing listeners for | |||
* {@link SelectionChangeEvent SelectionChangeEvents}. | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface SelectionChangeNotifier extends Serializable { | |||
/** | |||
* Registers a new selection change listener | |||
* | |||
* @param listener | |||
* the listener to register | |||
*/ | |||
void addSelectionChangeListener(SelectionChangeListener listener); | |||
/** | |||
* Removes a previously registered selection change listener | |||
* | |||
* @param listener | |||
* the listener to remove | |||
*/ | |||
void removeSelectionChangeListener(SelectionChangeListener listener); | |||
} |
@@ -0,0 +1,234 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.io.Serializable; | |||
import java.util.Collection; | |||
import com.vaadin.ui.components.grid.Grid; | |||
/** | |||
* The server-side interface that controls Grid's selection state. | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface SelectionModel extends Serializable { | |||
/** | |||
* Checks whether an item is selected or not. | |||
* | |||
* @param itemId | |||
* the item id to check for | |||
* @return <code>true</code> iff the item is selected | |||
*/ | |||
boolean isSelected(Object itemId); | |||
/** | |||
* Returns a collection of all the currently selected itemIds. | |||
* | |||
* @return a collection of all the currently selected itemIds | |||
*/ | |||
Collection<Object> getSelectedRows(); | |||
/** | |||
* Injects the current {@link Grid} instance into the SelectionModel. | |||
* <p> | |||
* <em>Note:</em> This method should not be called manually. | |||
* | |||
* @param grid | |||
* the Grid in which the SelectionModel currently is, or | |||
* <code>null</code> when a selection model is being detached | |||
* from a Grid. | |||
*/ | |||
void setGrid(Grid grid); | |||
/** | |||
* Resets the SelectiomModel to an initial state. | |||
* <p> | |||
* Most often this means that the selection state is cleared, but | |||
* implementations are free to interpret the "initial state" as they wish. | |||
* Some, for example, may want to keep the first selected item as selected. | |||
*/ | |||
void reset(); | |||
/** | |||
* A SelectionModel that supports multiple selections to be made. | |||
* <p> | |||
* This interface has a contract of having the same behavior, no matter how | |||
* the selection model is interacted with. In other words, if something is | |||
* forbidden to do in e.g. the user interface, it must also be forbidden to | |||
* do in the server-side and client-side APIs. | |||
*/ | |||
public interface Multi extends SelectionModel { | |||
/** | |||
* Marks items as selected. | |||
* <p> | |||
* This method does not clear any previous selection state, only adds to | |||
* it. | |||
* | |||
* @param itemIds | |||
* the itemId(s) to mark as selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if all the given itemIds already were | |||
* selected | |||
* @throws IllegalArgumentException | |||
* if the <code>itemIds</code> varargs array is | |||
* <code>null</code> | |||
* @see #deselect(Object...) | |||
*/ | |||
boolean select(Object... itemIds) throws IllegalArgumentException; | |||
/** | |||
* Marks items as selected. | |||
* <p> | |||
* This method does not clear any previous selection state, only adds to | |||
* it. | |||
* | |||
* @param itemIds | |||
* the itemIds to mark as selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if all the given itemIds already were | |||
* selected | |||
* @throws IllegalArgumentException | |||
* if <code>itemIds</code> is <code>null</code> | |||
* @see #deselect(Collection) | |||
*/ | |||
boolean select(Collection<?> itemIds) throws IllegalArgumentException; | |||
/** | |||
* Marks items as deselected. | |||
* | |||
* @param itemIds | |||
* the itemId(s) to remove from being selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if none the given itemIds were selected | |||
* previously | |||
* @throws IllegalArgumentException | |||
* if the <code>itemIds</code> varargs array is | |||
* <code>null</code> | |||
* @see #select(Object...) | |||
*/ | |||
boolean deselect(Object... itemIds) throws IllegalArgumentException; | |||
/** | |||
* Marks items as deselected. | |||
* | |||
* @param itemIds | |||
* the itemId(s) to remove from being selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if none the given itemIds were selected | |||
* previously | |||
* @throws IllegalArgumentException | |||
* if <code>itemIds</code> is <code>null</code> | |||
* @see #select(Collection) | |||
*/ | |||
boolean deselect(Collection<?> itemIds) throws IllegalArgumentException; | |||
/** | |||
* Marks all the items in the current Container as selected | |||
* | |||
* @return <code>true</code> iff some items were previously not selected | |||
* @see #deselectAll() | |||
*/ | |||
boolean selectAll(); | |||
/** | |||
* Marks all the items in the current Container as deselected | |||
* | |||
* @return <code>true</code> iff some items were previously selected | |||
* @see #selectAll() | |||
*/ | |||
boolean deselectAll(); | |||
} | |||
/** | |||
* A SelectionModel that supports for only single rows to be selected at a | |||
* time. | |||
* <p> | |||
* This interface has a contract of having the same behavior, no matter how | |||
* the selection model is interacted with. In other words, if something is | |||
* forbidden to do in e.g. the user interface, it must also be forbidden to | |||
* do in the server-side and client-side APIs. | |||
*/ | |||
public interface Single extends SelectionModel { | |||
/** | |||
* Marks an item as selected. | |||
* | |||
* @param itemIds | |||
* the itemId to mark as selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if the itemId already was selected | |||
* @throws IllegalStateException | |||
* if the selection was illegal. One such reason might be | |||
* that the implementation already had an item selected, and | |||
* that needs to be explicitly deselected before | |||
* re-selecting something | |||
* @see #deselect(Object) | |||
*/ | |||
boolean select(Object itemId) throws IllegalStateException; | |||
/** | |||
* Marks an item as deselected. | |||
* | |||
* @param itemId | |||
* the itemId to remove from being selected | |||
* @return <code>true</code> if the selection state changed. | |||
* <code>false</code> if the itemId already was selected | |||
* @throws IllegalStateException | |||
* if the deselection was illegal. One such reason might be | |||
* that the implementation enforces that an item is always | |||
* selected | |||
* @see #select(Object) | |||
*/ | |||
boolean deselect(Object itemId) throws IllegalStateException; | |||
/** | |||
* Gets the item id of the currently selected item. | |||
* | |||
* @return the item id of the currently selected item, or | |||
* <code>null</code> if nothing is selected | |||
*/ | |||
Object getSelectedRow(); | |||
} | |||
/** | |||
* A SelectionModel that does not allow for rows to be selected. | |||
* <p> | |||
* This interface has a contract of having the same behavior, no matter how | |||
* the selection model is interacted with. In other words, if the developer | |||
* is unable to select something programmatically, it is not allowed for the | |||
* end-user to select anything, either. | |||
*/ | |||
public interface None extends SelectionModel { | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return always <code>false</code>. | |||
*/ | |||
@Override | |||
public boolean isSelected(Object itemId); | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return always an empty collection. | |||
*/ | |||
@Override | |||
public Collection<Object> getSelectedRows(); | |||
} | |||
} |
@@ -0,0 +1,81 @@ | |||
/* | |||
* Copyright 2000-2013 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.ui.components.grid.selection; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
/** | |||
* A default implementation of a {@link SelectionModel.Single} | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class SingleSelectionModel extends AbstractSelectionModel implements | |||
SelectionModel.Single { | |||
@Override | |||
public boolean select(final Object itemId) { | |||
final Object selectedRow = getSelectedRow(); | |||
final boolean modified = selection.add(itemId); | |||
if (modified) { | |||
final Collection<Object> deselected; | |||
if (selectedRow != null) { | |||
deselectInternal(selectedRow, false); | |||
deselected = Collections.singleton(selectedRow); | |||
} else { | |||
deselected = Collections.emptySet(); | |||
} | |||
fireSelectionChangeEvent(deselected, selection); | |||
} | |||
return modified; | |||
} | |||
@Override | |||
public boolean deselect(final Object itemId) { | |||
return deselectInternal(itemId, true); | |||
} | |||
private boolean deselectInternal(final Object itemId, | |||
boolean fireEventIfNeeded) { | |||
final boolean modified = selection.remove(itemId); | |||
if (fireEventIfNeeded && modified) { | |||
fireSelectionChangeEvent(Collections.singleton(itemId), | |||
Collections.emptySet()); | |||
} | |||
return modified; | |||
} | |||
@Override | |||
public Object getSelectedRow() { | |||
if (selection.isEmpty()) { | |||
return null; | |||
} else { | |||
return selection.iterator().next(); | |||
} | |||
} | |||
/** | |||
* Resets the selection state. | |||
* <p> | |||
* If an item is selected, it will become deselected. | |||
*/ | |||
@Override | |||
public void reset() { | |||
deselect(getSelectedRow()); | |||
} | |||
} |
@@ -0,0 +1,306 @@ | |||
/* | |||
* Copyright 2000-2013 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.tests.server.component.grid; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import java.util.Collection; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import com.vaadin.data.util.IndexedContainer; | |||
import com.vaadin.ui.components.grid.Grid; | |||
import com.vaadin.ui.components.grid.Grid.SelectionMode; | |||
import com.vaadin.ui.components.grid.selection.SelectionChangeEvent; | |||
import com.vaadin.ui.components.grid.selection.SelectionChangeListener; | |||
import com.vaadin.ui.components.grid.selection.SelectionModel; | |||
/** | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridSelection { | |||
private static class MockSelectionChangeListener implements | |||
SelectionChangeListener { | |||
private SelectionChangeEvent event; | |||
@Override | |||
public void selectionChange(final SelectionChangeEvent event) { | |||
this.event = event; | |||
} | |||
public Collection<?> getAdded() { | |||
return event.getAdded(); | |||
} | |||
public Collection<?> getRemoved() { | |||
return event.getRemoved(); | |||
} | |||
public void clearEvent() { | |||
/* | |||
* This method is not strictly needed as the event will simply be | |||
* overridden, but it's good practice, and makes the code more | |||
* obvious. | |||
*/ | |||
event = null; | |||
} | |||
public boolean eventHasHappened() { | |||
return event != null; | |||
} | |||
} | |||
private Grid grid; | |||
private MockSelectionChangeListener mockListener; | |||
private final Object itemId1Present = "itemId1Present"; | |||
private final Object itemId2Present = "itemId2Present"; | |||
private final Object itemId1NotPresent = "itemId1NotPresent"; | |||
private final Object itemId2NotPresent = "itemId2NotPresent"; | |||
@Before | |||
public void setup() { | |||
final IndexedContainer container = new IndexedContainer(); | |||
container.addItem(itemId1Present); | |||
container.addItem(itemId2Present); | |||
for (int i = 2; i < 10; i++) { | |||
container.addItem(new Object()); | |||
} | |||
assertEquals("init size", 10, container.size()); | |||
assertTrue("itemId1Present", container.containsId(itemId1Present)); | |||
assertTrue("itemId2Present", container.containsId(itemId2Present)); | |||
assertFalse("itemId1NotPresent", | |||
container.containsId(itemId1NotPresent)); | |||
assertFalse("itemId2NotPresent", | |||
container.containsId(itemId2NotPresent)); | |||
grid = new Grid(container); | |||
mockListener = new MockSelectionChangeListener(); | |||
grid.addSelectionChangeListener(mockListener); | |||
assertFalse("eventHasHappened", mockListener.eventHasHappened()); | |||
} | |||
@Test | |||
public void defaultSelectionModeIsMulti() { | |||
assertTrue(grid.getSelectionModel() instanceof SelectionModel.Multi); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void getSelectedRowThrowsExceptionMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
grid.getSelectedRow(); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void getSelectedRowThrowsExceptionNone() { | |||
grid.setSelectionMode(SelectionMode.NONE); | |||
grid.getSelectedRow(); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void selectThrowsExceptionNone() { | |||
grid.setSelectionMode(SelectionMode.NONE); | |||
grid.select(itemId1Present); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void deselectRowThrowsExceptionNone() { | |||
grid.setSelectionMode(SelectionMode.NONE); | |||
grid.deselect(itemId1Present); | |||
} | |||
@Test | |||
public void selectionModeMapsToMulti() { | |||
assertTrue(grid.setSelectionMode(SelectionMode.MULTI) instanceof SelectionModel.Multi); | |||
} | |||
@Test | |||
public void selectionModeMapsToSingle() { | |||
assertTrue(grid.setSelectionMode(SelectionMode.SINGLE) instanceof SelectionModel.Single); | |||
} | |||
@Test | |||
public void selectionModeMapsToNone() { | |||
assertTrue(grid.setSelectionMode(SelectionMode.NONE) instanceof SelectionModel.None); | |||
} | |||
@Test(expected = IllegalArgumentException.class) | |||
public void selectionModeNullThrowsException() { | |||
grid.setSelectionMode(null); | |||
} | |||
@Test | |||
public void noSelectModel_isSelected() { | |||
grid.setSelectionMode(SelectionMode.NONE); | |||
assertFalse("itemId1Present", grid.isSelected(itemId1Present)); | |||
assertFalse("itemId1NotPresent", grid.isSelected(itemId1NotPresent)); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void noSelectModel_getSelectedRow() { | |||
grid.setSelectionMode(SelectionMode.NONE); | |||
grid.getSelectedRow(); | |||
} | |||
@Test | |||
public void noSelectModel_getSelectedRows() { | |||
grid.setSelectionMode(SelectionMode.NONE); | |||
assertTrue(grid.getSelectedRows().isEmpty()); | |||
} | |||
@Test | |||
public void selectionCallsListenerMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
selectionCallsListener(); | |||
} | |||
@Test | |||
public void selectionCallsListenerSingle() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
selectionCallsListener(); | |||
} | |||
private void selectionCallsListener() { | |||
grid.select(itemId1Present); | |||
assertEquals("added size", 1, mockListener.getAdded().size()); | |||
assertEquals("added item", itemId1Present, mockListener.getAdded() | |||
.iterator().next()); | |||
assertEquals("removed size", 0, mockListener.getRemoved().size()); | |||
} | |||
@Test | |||
public void deselectionCallsListenerMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
deselectionCallsListener(); | |||
} | |||
@Test | |||
public void deselectionCallsListenerSingle() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
deselectionCallsListener(); | |||
} | |||
private void deselectionCallsListener() { | |||
grid.select(itemId1Present); | |||
mockListener.clearEvent(); | |||
grid.deselect(itemId1Present); | |||
assertEquals("removed size", 1, mockListener.getRemoved().size()); | |||
assertEquals("removed item", itemId1Present, mockListener.getRemoved() | |||
.iterator().next()); | |||
assertEquals("removed size", 0, mockListener.getAdded().size()); | |||
} | |||
@Test | |||
public void deselectPresentButNotSelectedItemIdShouldntFireListenerMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
deselectPresentButNotSelectedItemIdShouldntFireListener(); | |||
} | |||
@Test | |||
public void deselectPresentButNotSelectedItemIdShouldntFireListenerSingle() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
deselectPresentButNotSelectedItemIdShouldntFireListener(); | |||
} | |||
private void deselectPresentButNotSelectedItemIdShouldntFireListener() { | |||
grid.deselect(itemId1Present); | |||
assertFalse(mockListener.eventHasHappened()); | |||
} | |||
@Test | |||
public void deselectNotPresentItemIdShouldNotThrowExceptionMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
grid.deselect(itemId1NotPresent); | |||
} | |||
@Test | |||
public void deselectNotPresentItemIdShouldNotThrowExceptionSingle() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
grid.deselect(itemId1NotPresent); | |||
} | |||
@Test | |||
public void selectNotPresentItemIdShouldNotThrowExceptionMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
grid.select(itemId1NotPresent); | |||
} | |||
@Test | |||
public void selectNotPresentItemIdShouldNotThrowExceptionSingle() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
grid.select(itemId1NotPresent); | |||
} | |||
@Test | |||
public void selectAllMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
final SelectionModel.Multi select = (SelectionModel.Multi) grid | |||
.getSelectionModel(); | |||
select.selectAll(); | |||
assertEquals("added size", 10, mockListener.getAdded().size()); | |||
assertEquals("removed size", 0, mockListener.getRemoved().size()); | |||
assertTrue("itemId1Present", | |||
mockListener.getAdded().contains(itemId1Present)); | |||
assertTrue("itemId2Present", | |||
mockListener.getAdded().contains(itemId2Present)); | |||
} | |||
@Test | |||
public void deselectAllMulti() { | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
final SelectionModel.Multi select = (SelectionModel.Multi) grid | |||
.getSelectionModel(); | |||
select.selectAll(); | |||
mockListener.clearEvent(); | |||
select.deselectAll(); | |||
assertEquals("removed size", 10, mockListener.getRemoved().size()); | |||
assertEquals("added size", 0, mockListener.getAdded().size()); | |||
assertTrue("itemId1Present", | |||
mockListener.getRemoved().contains(itemId1Present)); | |||
assertTrue("itemId2Present", | |||
mockListener.getRemoved().contains(itemId2Present)); | |||
assertTrue("selectedRows is empty", grid.getSelectedRows().isEmpty()); | |||
} | |||
@Test | |||
public void reselectionDeselectsPreviousSingle() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
grid.select(itemId1Present); | |||
mockListener.clearEvent(); | |||
grid.select(itemId2Present); | |||
assertEquals("added size", 1, mockListener.getAdded().size()); | |||
assertEquals("removed size", 1, mockListener.getRemoved().size()); | |||
assertEquals("added item", itemId2Present, mockListener.getAdded() | |||
.iterator().next()); | |||
assertEquals("removed item", itemId1Present, mockListener.getRemoved() | |||
.iterator().next()); | |||
assertEquals("selectedRows is correct", itemId2Present, | |||
grid.getSelectedRow()); | |||
} | |||
} |