This patch removes all selection related variables and API from several core parts of Grid. Change-Id: Idb7aa48fda69ded1ef58a69c1f7dbc78b7f52a54tags/7.6.0.alpha5
@@ -119,6 +119,8 @@ | |||
This may interfere with custom response compression solutions that do not respect the Content-Encoding response header.</li> | |||
<li>Unused methods related to the "out of sync" message have been removed from SystemMessages class.</li> | |||
<li>All notifications use the WAI-ARIA alert role to be compatible with Jaws</li> | |||
<li>Grid SelectionModels are now Extensions. This update removes all selection related variables and API from | |||
GridConnector, GridState, GridServerRpc and GridClientRpc</li> | |||
</ul> | |||
<h3 id="knownissues">Known Issues and Limitations</h3> | |||
<ul> |
@@ -0,0 +1,82 @@ | |||
/* | |||
* Copyright 2000-2014 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.connectors; | |||
import java.util.Collection; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.extensions.AbstractExtensionConnector; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Base class for all selection model connectors. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public abstract class AbstractSelectionModelConnector<T extends SelectionModel<JsonObject>> | |||
extends AbstractExtensionConnector { | |||
@Override | |||
public GridConnector getParent() { | |||
return (GridConnector) super.getParent(); | |||
} | |||
protected Grid<JsonObject> getGrid() { | |||
return getParent().getWidget(); | |||
} | |||
protected RowHandle<JsonObject> getRowHandle(JsonObject row) { | |||
return getGrid().getDataSource().getHandle(row); | |||
} | |||
protected String getRowKey(JsonObject row) { | |||
return row != null ? getParent().getRowKey(row) : null; | |||
} | |||
protected abstract T createSelectionModel(); | |||
public abstract static class AbstractSelectionModel implements | |||
SelectionModel<JsonObject> { | |||
@Override | |||
public boolean isSelected(JsonObject row) { | |||
return row.hasKey(GridState.JSONKEY_SELECTED); | |||
} | |||
@Override | |||
public void setGrid(Grid<JsonObject> grid) { | |||
// NO-OP | |||
} | |||
@Override | |||
public void reset() { | |||
// Should not need any actions. | |||
} | |||
@Override | |||
public Collection<JsonObject> getSelectedRows() { | |||
throw new UnsupportedOperationException( | |||
"This client-side selection model " | |||
+ getClass().getSimpleName() | |||
+ " does not know selected rows."); | |||
} | |||
} | |||
} |
@@ -22,7 +22,6 @@ import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
@@ -36,7 +35,6 @@ import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.ui.CheckBox; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.ComponentConnector; | |||
import com.vaadin.client.ConnectorHierarchyChangeEvent; | |||
@@ -48,8 +46,6 @@ import com.vaadin.client.communication.StateChangeEvent; | |||
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; | |||
import com.vaadin.client.connectors.RpcDataSourceConnector.DetailsListener; | |||
import com.vaadin.client.connectors.RpcDataSourceConnector.RpcDataSource; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.ui.AbstractFieldConnector; | |||
import com.vaadin.client.ui.AbstractHasComponentsConnector; | |||
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; | |||
@@ -72,15 +68,6 @@ import com.vaadin.client.widget.grid.events.EditorMoveEvent; | |||
import com.vaadin.client.widget.grid.events.EditorOpenEvent; | |||
import com.vaadin.client.widget.grid.events.GridClickEvent; | |||
import com.vaadin.client.widget.grid.events.GridDoubleClickEvent; | |||
import com.vaadin.client.widget.grid.events.SelectAllEvent; | |||
import com.vaadin.client.widget.grid.events.SelectAllHandler; | |||
import com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel; | |||
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.SelectionModelMulti; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelNone; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelSingle; | |||
import com.vaadin.client.widget.grid.sort.SortEvent; | |||
import com.vaadin.client.widget.grid.sort.SortHandler; | |||
import com.vaadin.client.widget.grid.sort.SortOrder; | |||
@@ -99,7 +86,6 @@ import com.vaadin.shared.ui.grid.GridColumnState; | |||
import com.vaadin.shared.ui.grid.GridConstants; | |||
import com.vaadin.shared.ui.grid.GridServerRpc; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; | |||
import com.vaadin.shared.ui.grid.GridStaticSectionState; | |||
import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; | |||
import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; | |||
@@ -584,19 +570,8 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
*/ | |||
private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<String, CustomGridColumn>(); | |||
private AbstractRowHandleSelectionModel<JsonObject> selectionModel; | |||
private Set<String> selectedKeys = new LinkedHashSet<String>(); | |||
private List<String> columnOrder = new ArrayList<String>(); | |||
/** | |||
* {@link #selectionUpdatedFromState} is set to true when | |||
* {@link #updateSelectionFromState()} makes changes to selection. This flag | |||
* tells the {@code internalSelectionChangeHandler} to not send same data | |||
* straight back to server. Said listener sets it back to false when | |||
* handling that event. | |||
*/ | |||
private boolean selectionUpdatedFromState; | |||
/** | |||
* {@link #columnsUpdatedFromState} is set to true when | |||
* {@link #updateColumnOrderFromState(List)} is updating the column order | |||
@@ -608,29 +583,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
private RpcDataSource dataSource; | |||
private SelectionHandler<JsonObject> internalSelectionChangeHandler = new SelectionHandler<JsonObject>() { | |||
@Override | |||
public void onSelect(SelectionEvent<JsonObject> event) { | |||
if (event.isBatchedSelection()) { | |||
return; | |||
} | |||
if (!selectionUpdatedFromState) { | |||
for (JsonObject row : event.getRemoved()) { | |||
selectedKeys.remove(dataSource.getRowKey(row)); | |||
} | |||
for (JsonObject row : event.getAdded()) { | |||
selectedKeys.add(dataSource.getRowKey(row)); | |||
} | |||
getRpcProxy(GridServerRpc.class).select( | |||
new ArrayList<String>(selectedKeys)); | |||
} else { | |||
selectionUpdatedFromState = false; | |||
} | |||
} | |||
}; | |||
/* Used to track Grid editor columns with validation errors */ | |||
private final Map<Column<?, JsonObject>, String> columnToErrorMessage = new HashMap<Column<?, JsonObject>, String>(); | |||
@@ -744,30 +696,8 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
public void recalculateColumnWidths() { | |||
getWidget().recalculateColumnWidths(); | |||
} | |||
@Override | |||
public void setSelectAll(boolean allSelected) { | |||
if (selectionModel instanceof SelectionModel.Multi | |||
&& selectionModel.getSelectionColumnRenderer() != null) { | |||
final Widget widget; | |||
try { | |||
HeaderRow defaultHeaderRow = getWidget() | |||
.getDefaultHeaderRow(); | |||
if (defaultHeaderRow != null) { | |||
widget = defaultHeaderRow.getCell( | |||
getWidget().getColumn(0)).getWidget(); | |||
((CheckBox) widget).setValue(allSelected, false); | |||
} | |||
} catch (Exception e) { | |||
getLogger().warning( | |||
"Problems finding select all checkbox."); | |||
} | |||
} | |||
} | |||
}); | |||
getWidget().addSelectionHandler(internalSelectionChangeHandler); | |||
/* Item click events */ | |||
getWidget().addBodyClickHandler(itemClickHandler); | |||
getWidget().addBodyDoubleClickHandler(itemClickHandler); | |||
@@ -800,15 +730,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
} | |||
}); | |||
getWidget().addSelectAllHandler(new SelectAllHandler<JsonObject>() { | |||
@Override | |||
public void onSelectAll(SelectAllEvent<JsonObject> event) { | |||
getRpcProxy(GridServerRpc.class).selectAll(); | |||
} | |||
}); | |||
getWidget().setEditorHandler(editorHandler); | |||
getWidget().addColumnReorderHandler(columnReorderHandler); | |||
getWidget().addColumnVisibilityChangeHandler( | |||
@@ -884,19 +805,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
updateFooterFromState(getState().footer); | |||
} | |||
// Selection | |||
if (stateChangeEvent.hasPropertyChanged("selectionMode")) { | |||
onSelectionModeChange(); | |||
updateSelectDeselectAllowed(); | |||
} else if (stateChangeEvent | |||
.hasPropertyChanged("singleSelectDeselectAllowed")) { | |||
updateSelectDeselectAllowed(); | |||
} | |||
if (stateChangeEvent.hasPropertyChanged("selectedKeys")) { | |||
updateSelectionFromState(); | |||
} | |||
// Sorting | |||
if (stateChangeEvent.hasPropertyChanged("sortColumns") | |||
|| stateChangeEvent.hasPropertyChanged("sortDirs")) { | |||
@@ -923,14 +831,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
} | |||
} | |||
private void updateSelectDeselectAllowed() { | |||
SelectionModel<JsonObject> model = getWidget().getSelectionModel(); | |||
if (model instanceof SelectionModel.Single<?>) { | |||
((SelectionModel.Single<?>) model) | |||
.setDeselectAllowed(getState().singleSelectDeselectAllowed); | |||
} | |||
} | |||
private void updateColumnOrderFromState(List<String> stateColumnOrder) { | |||
CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder | |||
.size()]; | |||
@@ -1102,20 +1002,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
columnOrder.add(state.id); | |||
} | |||
/** | |||
* If we have a selection column renderer, we need to offset the index by | |||
* one when referring to the column index in the widget. | |||
*/ | |||
private int getWidgetColumnIndex(final int columnIndex) { | |||
Renderer<Boolean> selectionColumnRenderer = getWidget() | |||
.getSelectionModel().getSelectionColumnRenderer(); | |||
int widgetColumnIndex = columnIndex; | |||
if (selectionColumnRenderer != null) { | |||
widgetColumnIndex++; | |||
} | |||
return widgetColumnIndex; | |||
} | |||
/** | |||
* Updates the column values from a state | |||
* | |||
@@ -1178,63 +1064,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
getWidget().setDataSource(this.dataSource); | |||
} | |||
private void onSelectionModeChange() { | |||
SharedSelectionMode mode = getState().selectionMode; | |||
if (mode == null) { | |||
getLogger().fine("ignored mode change"); | |||
return; | |||
} | |||
AbstractRowHandleSelectionModel<JsonObject> model = createSelectionModel(mode); | |||
if (selectionModel == null | |||
|| !model.getClass().equals(selectionModel.getClass())) { | |||
selectionModel = model; | |||
getWidget().setSelectionModel(model); | |||
selectedKeys.clear(); | |||
} | |||
} | |||
private void updateSelectionFromState() { | |||
boolean changed = false; | |||
List<String> stateKeys = getState().selectedKeys; | |||
// find new deselections | |||
for (String key : selectedKeys) { | |||
if (!stateKeys.contains(key)) { | |||
changed = true; | |||
deselectByHandle(dataSource.getHandleByKey(key)); | |||
} | |||
} | |||
// find new selections | |||
for (String key : stateKeys) { | |||
if (!selectedKeys.contains(key)) { | |||
changed = true; | |||
selectByHandle(dataSource.getHandleByKey(key)); | |||
} | |||
} | |||
/* | |||
* A defensive copy in case the collection in the state is mutated | |||
* instead of re-assigned. | |||
*/ | |||
selectedKeys = new LinkedHashSet<String>(stateKeys); | |||
/* | |||
* We need to fire this event so that Grid is able to re-render the | |||
* selection changes (if applicable). | |||
*/ | |||
if (changed) { | |||
// At least for now there's no way to send the selected and/or | |||
// deselected row data. Some data is only stored as keys | |||
selectionUpdatedFromState = true; | |||
getWidget().fireEvent( | |||
new SelectionEvent<JsonObject>(getWidget(), | |||
(List<JsonObject>) null, null, false)); | |||
} | |||
} | |||
private void onSortStateChange() { | |||
List<SortOrder> sortOrder = new ArrayList<SortOrder>(); | |||
@@ -1253,41 +1082,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
return Logger.getLogger(getClass().getName()); | |||
} | |||
@SuppressWarnings("static-method") | |||
private AbstractRowHandleSelectionModel<JsonObject> createSelectionModel( | |||
SharedSelectionMode mode) { | |||
switch (mode) { | |||
case SINGLE: | |||
return new SelectionModelSingle<JsonObject>(); | |||
case MULTI: | |||
return new SelectionModelMulti<JsonObject>(); | |||
case NONE: | |||
return new SelectionModelNone<JsonObject>(); | |||
default: | |||
throw new IllegalStateException("unexpected mode value: " + mode); | |||
} | |||
} | |||
/** | |||
* A workaround method for accessing the protected method | |||
* {@code AbstractRowHandleSelectionModel.selectByHandle} | |||
*/ | |||
private native void selectByHandle(RowHandle<JsonObject> handle) | |||
/*-{ | |||
var model = this.@com.vaadin.client.connectors.GridConnector::selectionModel; | |||
model.@com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel::selectByHandle(*)(handle); | |||
}-*/; | |||
/** | |||
* A workaround method for accessing the protected method | |||
* {@code AbstractRowHandleSelectionModel.deselectByHandle} | |||
*/ | |||
private native void deselectByHandle(RowHandle<JsonObject> handle) | |||
/*-{ | |||
var model = this.@com.vaadin.client.connectors.GridConnector::selectionModel; | |||
model.@com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel::deselectByHandle(*)(handle); | |||
}-*/; | |||
/** | |||
* Gets the row key for a row object. | |||
* | |||
@@ -1312,7 +1106,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
@Override | |||
public void updateCaption(ComponentConnector connector) { | |||
// TODO Auto-generated method stub | |||
} | |||
@Override |
@@ -0,0 +1,360 @@ | |||
/* | |||
* Copyright 2000-2014 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.connectors; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.ui.CheckBox; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.annotations.OnStateChange; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.renderers.ComplexRenderer; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widget.grid.DataAvailableEvent; | |||
import com.vaadin.client.widget.grid.DataAvailableHandler; | |||
import com.vaadin.client.widget.grid.events.SelectAllEvent; | |||
import com.vaadin.client.widget.grid.events.SelectAllHandler; | |||
import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; | |||
import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.client.widgets.Grid.HeaderCell; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; | |||
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; | |||
import com.vaadin.ui.Grid.MultiSelectionModel; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Connector for server-side {@link MultiSelectionModel}. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
@Connect(MultiSelectionModel.class) | |||
public class MultiSelectionModelConnector extends | |||
AbstractSelectionModelConnector<SelectionModel.Multi<JsonObject>> { | |||
private Multi<JsonObject> selectionModel = createSelectionModel(); | |||
private SpaceSelectHandler<JsonObject> spaceHandler; | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
getGrid().setSelectionModel(selectionModel); | |||
spaceHandler = new SpaceSelectHandler<JsonObject>(getGrid()); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
spaceHandler.removeHandler(); | |||
} | |||
@Override | |||
protected Multi<JsonObject> createSelectionModel() { | |||
return new MultiSelectionModel(); | |||
} | |||
@Override | |||
public MultiSelectionModelState getState() { | |||
return (MultiSelectionModelState) super.getState(); | |||
} | |||
@OnStateChange("allSelected") | |||
void updateSelectAllCheckbox() { | |||
if (selectionModel.getSelectionColumnRenderer() != null) { | |||
HeaderCell cell = getGrid().getDefaultHeaderRow().getCell( | |||
getGrid().getColumn(0)); | |||
CheckBox widget = (CheckBox) cell.getWidget(); | |||
widget.setValue(getState().allSelected, false); | |||
} | |||
} | |||
protected class MultiSelectionModel extends AbstractSelectionModel | |||
implements SelectionModel.Multi.Batched<JsonObject> { | |||
private ComplexRenderer<Boolean> renderer = null; | |||
private Set<RowHandle<JsonObject>> selected = new HashSet<RowHandle<JsonObject>>(); | |||
private Set<RowHandle<JsonObject>> deselected = new HashSet<RowHandle<JsonObject>>(); | |||
private HandlerRegistration selectAll; | |||
private HandlerRegistration dataAvailable; | |||
private Range availableRows; | |||
private boolean batchSelect = false; | |||
@Override | |||
public void setGrid(Grid<JsonObject> grid) { | |||
super.setGrid(grid); | |||
if (grid != null) { | |||
renderer = createSelectionColumnRenderer(grid); | |||
selectAll = getGrid().addSelectAllHandler( | |||
new SelectAllHandler<JsonObject>() { | |||
@Override | |||
public void onSelectAll( | |||
SelectAllEvent<JsonObject> event) { | |||
selectAll(); | |||
} | |||
}); | |||
dataAvailable = getGrid().addDataAvailableHandler( | |||
new DataAvailableHandler() { | |||
@Override | |||
public void onDataAvailable(DataAvailableEvent event) { | |||
availableRows = event.getAvailableRows(); | |||
} | |||
}); | |||
} else if (renderer != null) { | |||
renderer.destroy(); | |||
selectAll.removeHandler(); | |||
dataAvailable.removeHandler(); | |||
renderer = null; | |||
} | |||
} | |||
/** | |||
* Creates a selection column renderer. This method can be overridden to | |||
* use a custom renderer or use {@code null} to disable the selection | |||
* column. | |||
* | |||
* @param grid | |||
* the grid for this selection model | |||
* @return selection column renderer or {@code null} if not needed | |||
*/ | |||
protected ComplexRenderer<Boolean> createSelectionColumnRenderer( | |||
Grid<JsonObject> grid) { | |||
return new MultiSelectionRenderer<JsonObject>(grid); | |||
} | |||
/** | |||
* Selects all available rows, sends request to server to select | |||
* everything. | |||
*/ | |||
public void selectAll() { | |||
assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; | |||
Set<RowHandle<JsonObject>> rows = new HashSet<DataSource.RowHandle<JsonObject>>(); | |||
DataSource<JsonObject> dataSource = getGrid().getDataSource(); | |||
for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) { | |||
final JsonObject row = dataSource.getRow(i); | |||
if (row != null) { | |||
RowHandle<JsonObject> handle = dataSource.getHandle(row); | |||
markAsSelected(handle, true); | |||
rows.add(handle); | |||
} | |||
} | |||
getRpcProxy(MultiSelectionModelServerRpc.class).selectAll(); | |||
cleanRowCache(rows); | |||
} | |||
@Override | |||
public Renderer<Boolean> getSelectionColumnRenderer() { | |||
return renderer; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return {@code false} if rows is empty, else {@code true} | |||
*/ | |||
@Override | |||
public boolean select(JsonObject... rows) { | |||
return select(Arrays.asList(rows)); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return {@code false} if rows is empty, else {@code true} | |||
*/ | |||
@Override | |||
public boolean deselect(JsonObject... rows) { | |||
return deselect(Arrays.asList(rows)); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return always {@code true} | |||
*/ | |||
@Override | |||
public boolean deselectAll() { | |||
assert !isBeingBatchSelected() : "Can't select all in middle of a batch selection."; | |||
Set<RowHandle<JsonObject>> rows = new HashSet<DataSource.RowHandle<JsonObject>>(); | |||
DataSource<JsonObject> dataSource = getGrid().getDataSource(); | |||
for (int i = availableRows.getStart(); i < availableRows.getEnd(); ++i) { | |||
final JsonObject row = dataSource.getRow(i); | |||
if (row != null) { | |||
RowHandle<JsonObject> handle = dataSource.getHandle(row); | |||
markAsSelected(handle, false); | |||
rows.add(handle); | |||
} | |||
} | |||
getRpcProxy(MultiSelectionModelServerRpc.class).deselectAll(); | |||
cleanRowCache(rows); | |||
return true; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return {@code false} if rows is empty, else {@code true} | |||
*/ | |||
@Override | |||
public boolean select(Collection<JsonObject> rows) { | |||
if (rows.isEmpty()) { | |||
return false; | |||
} | |||
for (JsonObject row : rows) { | |||
RowHandle<JsonObject> rowHandle = getRowHandle(row); | |||
markAsSelected(rowHandle, true); | |||
selected.add(rowHandle); | |||
} | |||
if (!isBeingBatchSelected()) { | |||
sendSelected(); | |||
} | |||
return true; | |||
} | |||
/** | |||
* Marks the JsonObject pointed by RowHandle to have selected | |||
* information equal to given boolean | |||
* | |||
* @param row | |||
* row handle | |||
* @param selected | |||
* should row be selected | |||
*/ | |||
protected void markAsSelected(RowHandle<JsonObject> row, | |||
boolean selected) { | |||
row.pin(); | |||
if (selected) { | |||
row.getRow().put(GridState.JSONKEY_SELECTED, true); | |||
} else { | |||
row.getRow().remove(GridState.JSONKEY_SELECTED); | |||
} | |||
row.updateRow(); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* | |||
* @return {@code false} if rows is empty, else {@code true} | |||
*/ | |||
@Override | |||
public boolean deselect(Collection<JsonObject> rows) { | |||
if (rows.isEmpty()) { | |||
return false; | |||
} | |||
for (JsonObject row : rows) { | |||
RowHandle<JsonObject> rowHandle = getRowHandle(row); | |||
markAsSelected(rowHandle, false); | |||
deselected.add(rowHandle); | |||
} | |||
if (!isBeingBatchSelected()) { | |||
sendDeselected(); | |||
} | |||
return true; | |||
} | |||
private void sendDeselected() { | |||
getRpcProxy(MultiSelectionModelServerRpc.class).deselect( | |||
getRowKeys(deselected)); | |||
cleanRowCache(deselected); | |||
} | |||
private void sendSelected() { | |||
getRpcProxy(MultiSelectionModelServerRpc.class).select( | |||
getRowKeys(selected)); | |||
cleanRowCache(selected); | |||
} | |||
private List<String> getRowKeys(Set<RowHandle<JsonObject>> handles) { | |||
List<String> keys = new ArrayList<String>(); | |||
for (RowHandle<JsonObject> handle : handles) { | |||
keys.add(getRowKey(handle.getRow())); | |||
} | |||
return keys; | |||
} | |||
private Set<JsonObject> getRows(Set<RowHandle<JsonObject>> handles) { | |||
Set<JsonObject> rows = new HashSet<JsonObject>(); | |||
for (RowHandle<JsonObject> handle : handles) { | |||
rows.add(handle.getRow()); | |||
} | |||
return rows; | |||
} | |||
private void cleanRowCache(Set<RowHandle<JsonObject>> handles) { | |||
for (RowHandle<JsonObject> handle : handles) { | |||
handle.unpin(); | |||
} | |||
handles.clear(); | |||
} | |||
@Override | |||
public void startBatchSelect() { | |||
assert selected.isEmpty() && deselected.isEmpty() : "Row caches were not clear."; | |||
batchSelect = true; | |||
} | |||
@Override | |||
public void commitBatchSelect() { | |||
assert batchSelect : "Not batch selecting."; | |||
if (!selected.isEmpty()) { | |||
sendSelected(); | |||
} | |||
if (!deselected.isEmpty()) { | |||
sendDeselected(); | |||
} | |||
batchSelect = false; | |||
} | |||
@Override | |||
public boolean isBeingBatchSelected() { | |||
return batchSelect; | |||
} | |||
@Override | |||
public Collection<JsonObject> getSelectedRowsBatch() { | |||
return Collections.unmodifiableSet(getRows(selected)); | |||
} | |||
@Override | |||
public Collection<JsonObject> getDeselectedRowsBatch() { | |||
return Collections.unmodifiableSet(getRows(deselected)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* Copyright 2000-2014 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.connectors; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelNone; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.ui.Grid.NoSelectionModel; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Connector for server-side {@link NoSelectionModel}. | |||
*/ | |||
@Connect(NoSelectionModel.class) | |||
public class NoSelectionModelConnector extends | |||
AbstractSelectionModelConnector<SelectionModel<JsonObject>> { | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
getGrid().setSelectionModel(createSelectionModel()); | |||
} | |||
@Override | |||
protected SelectionModel<JsonObject> createSelectionModel() { | |||
return new SelectionModelNone<JsonObject>(); | |||
} | |||
} |
@@ -99,8 +99,10 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { | |||
} | |||
@Override | |||
public void updateRowData(JsonObject row) { | |||
RpcDataSource.this.updateRowData(row); | |||
public void updateRowData(JsonArray rowArray) { | |||
for (int i = 0; i < rowArray.length(); ++i) { | |||
RpcDataSource.this.updateRowData(rowArray.getObject(i)); | |||
} | |||
} | |||
}); | |||
} |
@@ -0,0 +1,148 @@ | |||
/* | |||
* Copyright 2000-2014 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.connectors; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.annotations.OnStateChange; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widget.grid.selection.ClickSelectHandler; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel.Single; | |||
import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; | |||
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; | |||
import com.vaadin.ui.Grid.SingleSelectionModel; | |||
import elemental.json.JsonObject; | |||
/** | |||
* Connector for server-side {@link SingleSelectionModel}. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
@Connect(SingleSelectionModel.class) | |||
public class SingleSelectionModelConnector extends | |||
AbstractSelectionModelConnector<SelectionModel.Single<JsonObject>> { | |||
private SpaceSelectHandler<JsonObject> spaceHandler; | |||
private ClickSelectHandler<JsonObject> clickHandler; | |||
private Single<JsonObject> selectionModel = createSelectionModel(); | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
getGrid().setSelectionModel(selectionModel); | |||
spaceHandler = new SpaceSelectHandler<JsonObject>(getGrid()); | |||
clickHandler = new ClickSelectHandler<JsonObject>(getGrid()); | |||
} | |||
@Override | |||
public SingleSelectionModelState getState() { | |||
return (SingleSelectionModelState) super.getState(); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
spaceHandler.removeHandler(); | |||
clickHandler.removeHandler(); | |||
super.onUnregister(); | |||
} | |||
@Override | |||
protected Single<JsonObject> createSelectionModel() { | |||
return new SingleSelectionModel(); | |||
} | |||
@OnStateChange("deselectAllowed") | |||
void updateDeselectAllowed() { | |||
selectionModel.setDeselectAllowed(getState().deselectAllowed); | |||
} | |||
/** | |||
* SingleSelectionModel without a selection column renderer. | |||
*/ | |||
public class SingleSelectionModel extends AbstractSelectionModel implements | |||
SelectionModel.Single<JsonObject> { | |||
private RowHandle<JsonObject> selectedRow; | |||
private boolean deselectAllowed; | |||
@Override | |||
public Renderer<Boolean> getSelectionColumnRenderer() { | |||
return null; | |||
} | |||
@Override | |||
public boolean select(JsonObject row) { | |||
boolean changed = false; | |||
if ((row == null && isDeselectAllowed()) | |||
|| (row != null && !getRowHandle(row).equals(selectedRow))) { | |||
if (selectedRow != null) { | |||
selectedRow.getRow().remove(GridState.JSONKEY_SELECTED); | |||
selectedRow.updateRow(); | |||
selectedRow.unpin(); | |||
selectedRow = null; | |||
changed = true; | |||
} | |||
if (row != null) { | |||
selectedRow = getRowHandle(row); | |||
selectedRow.pin(); | |||
selectedRow.getRow().put(GridState.JSONKEY_SELECTED, true); | |||
selectedRow.updateRow(); | |||
changed = true; | |||
} | |||
} | |||
if (changed) { | |||
getRpcProxy(SingleSelectionModelServerRpc.class).select( | |||
getRowKey(row)); | |||
} | |||
return changed; | |||
} | |||
@Override | |||
public boolean deselect(JsonObject row) { | |||
if (getRowHandle(row).equals(selectedRow)) { | |||
select(null); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public JsonObject getSelectedRow() { | |||
throw new UnsupportedOperationException( | |||
"This client-side selection model " | |||
+ getClass().getSimpleName() | |||
+ " does not know selected row."); | |||
} | |||
@Override | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
this.deselectAllowed = deselectAllowed; | |||
} | |||
@Override | |||
public boolean isDeselectAllowed() { | |||
return deselectAllowed; | |||
} | |||
} | |||
} |
@@ -5287,7 +5287,7 @@ public class Grid<T> extends ResizeComposite implements | |||
@Override | |||
public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) { | |||
for (FlyweightCell cell : cellsToDetach) { | |||
Renderer renderer = findRenderer(cell); | |||
Renderer<?> renderer = findRenderer(cell); | |||
if (renderer instanceof WidgetRenderer) { | |||
try { | |||
Widget w = WidgetUtil.findWidget(cell.getElement() | |||
@@ -5317,7 +5317,7 @@ public class Grid<T> extends ResizeComposite implements | |||
// any more | |||
rowReference.set(rowIndex, null, row.getElement()); | |||
for (FlyweightCell cell : detachedCells) { | |||
Renderer renderer = findRenderer(cell); | |||
Renderer<?> renderer = findRenderer(cell); | |||
if (renderer instanceof ComplexRenderer) { | |||
try { | |||
Column<?, T> column = getVisibleColumn(cell.getColumn()); | |||
@@ -7233,12 +7233,12 @@ public class Grid<T> extends ResizeComposite implements | |||
* if the current selection model is not an instance of | |||
* {@link SelectionModel.Single} or {@link SelectionModel.Multi} | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public boolean select(T row) { | |||
if (selectionModel instanceof SelectionModel.Single<?>) { | |||
return ((SelectionModel.Single<T>) selectionModel).select(row); | |||
} else if (selectionModel instanceof SelectionModel.Multi<?>) { | |||
return ((SelectionModel.Multi<T>) selectionModel).select(row); | |||
return ((SelectionModel.Multi<T>) selectionModel) | |||
.select(Collections.singleton(row)); | |||
} else { | |||
throw new IllegalStateException("Unsupported selection model"); | |||
} | |||
@@ -7258,12 +7258,12 @@ public class Grid<T> extends ResizeComposite implements | |||
* if the current selection model is not an instance of | |||
* {@link SelectionModel.Single} or {@link SelectionModel.Multi} | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public boolean deselect(T row) { | |||
if (selectionModel instanceof SelectionModel.Single<?>) { | |||
return ((SelectionModel.Single<T>) selectionModel).deselect(row); | |||
} else if (selectionModel instanceof SelectionModel.Multi<?>) { | |||
return ((SelectionModel.Multi<T>) selectionModel).deselect(row); | |||
return ((SelectionModel.Multi<T>) selectionModel) | |||
.deselect(Collections.singleton(row)); | |||
} else { | |||
throw new IllegalStateException("Unsupported selection model"); | |||
} |
@@ -744,15 +744,11 @@ public class RpcDataProviderExtension extends AbstractExtension { | |||
// Send current rows again if needed. | |||
if (refreshCache) { | |||
for (Object itemId : activeItemHandler.getActiveItemIds()) { | |||
internalUpdateRowData(itemId); | |||
} | |||
updatedItemIds.addAll(activeItemHandler.getActiveItemIds()); | |||
} | |||
} | |||
for (Object itemId : updatedItemIds) { | |||
internalUpdateRowData(itemId); | |||
} | |||
internalUpdateRows(updatedItemIds); | |||
// Clear all changes. | |||
rowChanges.clear(); | |||
@@ -913,11 +909,20 @@ public class RpcDataProviderExtension extends AbstractExtension { | |||
updatedItemIds.add(itemId); | |||
} | |||
private void internalUpdateRowData(Object itemId) { | |||
if (activeItemHandler.getActiveItemIds().contains(itemId)) { | |||
JsonObject row = getRowData(getGrid().getColumns(), itemId); | |||
rpc.updateRowData(row); | |||
private void internalUpdateRows(Set<Object> itemIds) { | |||
if (itemIds.isEmpty()) { | |||
return; | |||
} | |||
JsonArray rowData = Json.createArray(); | |||
int i = 0; | |||
for (Object itemId : itemIds) { | |||
if (activeItemHandler.getActiveItemIds().contains(itemId)) { | |||
JsonObject row = getRowData(getGrid().getColumns(), itemId); | |||
rowData.set(i++, row); | |||
} | |||
} | |||
rpc.updateRowData(rowData); | |||
} | |||
/** |
@@ -82,6 +82,7 @@ import com.vaadin.server.AbstractClientConnector; | |||
import com.vaadin.server.AbstractExtension; | |||
import com.vaadin.server.EncodeResult; | |||
import com.vaadin.server.ErrorMessage; | |||
import com.vaadin.server.Extension; | |||
import com.vaadin.server.JsonCodec; | |||
import com.vaadin.server.KeyMapper; | |||
import com.vaadin.server.VaadinSession; | |||
@@ -94,13 +95,16 @@ import com.vaadin.shared.ui.grid.GridColumnState; | |||
import com.vaadin.shared.ui.grid.GridConstants; | |||
import com.vaadin.shared.ui.grid.GridServerRpc; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; | |||
import com.vaadin.shared.ui.grid.GridStaticCellType; | |||
import com.vaadin.shared.ui.grid.GridStaticSectionState; | |||
import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; | |||
import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; | |||
import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.shared.ui.grid.ScrollDestination; | |||
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; | |||
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; | |||
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; | |||
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
@@ -111,7 +115,6 @@ import com.vaadin.ui.renderers.TextRenderer; | |||
import com.vaadin.util.ReflectTools; | |||
import elemental.json.Json; | |||
import elemental.json.JsonArray; | |||
import elemental.json.JsonObject; | |||
import elemental.json.JsonValue; | |||
@@ -710,8 +713,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
/** | |||
* The server-side interface that controls Grid's selection state. | |||
* SelectionModel should extend {@link AbstractGridExtension}. | |||
*/ | |||
public interface SelectionModel extends Serializable { | |||
public interface SelectionModel extends Serializable, Extension { | |||
/** | |||
* Checks whether an item is selected or not. | |||
* | |||
@@ -730,6 +734,8 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
/** | |||
* Injects the current {@link Grid} instance into the SelectionModel. | |||
* This method should usually call the extend method of | |||
* {@link AbstractExtension}. | |||
* <p> | |||
* <em>Note:</em> This method should not be called manually. | |||
* | |||
@@ -971,10 +977,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
* A base class for SelectionModels that contains some of the logic that is | |||
* reusable. | |||
*/ | |||
public static abstract class AbstractSelectionModel implements | |||
SelectionModel { | |||
public static abstract class AbstractSelectionModel extends | |||
AbstractGridExtension implements SelectionModel, DataGenerator { | |||
protected final LinkedHashSet<Object> selection = new LinkedHashSet<Object>(); | |||
protected Grid grid = null; | |||
@Override | |||
public boolean isSelected(final Object itemId) { | |||
@@ -988,7 +993,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
@Override | |||
public void setGrid(final Grid grid) { | |||
this.grid = grid; | |||
if (grid != null) { | |||
extend(grid); | |||
} | |||
} | |||
/** | |||
@@ -1002,7 +1009,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
*/ | |||
protected void checkItemIdExists(Object itemId) | |||
throws IllegalArgumentException { | |||
if (!grid.getContainerDataSource().containsId(itemId)) { | |||
if (!getParentGrid().getContainerDataSource().containsId(itemId)) { | |||
throw new IllegalArgumentException("Given item id (" + itemId | |||
+ ") does not exist in the container"); | |||
} | |||
@@ -1044,7 +1051,19 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
protected void fireSelectionEvent( | |||
final Collection<Object> oldSelection, | |||
final Collection<Object> newSelection) { | |||
grid.fireSelectionEvent(oldSelection, newSelection); | |||
getParentGrid().fireSelectionEvent(oldSelection, newSelection); | |||
} | |||
@Override | |||
public void generateData(Object itemId, Item item, JsonObject rowData) { | |||
if (isSelected(itemId)) { | |||
rowData.put(GridState.JSONKEY_SELECTED, true); | |||
} | |||
} | |||
@Override | |||
protected Object getItemId(String rowKey) { | |||
return rowKey != null ? super.getItemId(rowKey) : null; | |||
} | |||
} | |||
@@ -1053,8 +1072,25 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
*/ | |||
public static class SingleSelectionModel extends AbstractSelectionModel | |||
implements SelectionModel.Single { | |||
@Override | |||
protected void extend(AbstractClientConnector target) { | |||
super.extend(target); | |||
registerRpc(new SingleSelectionModelServerRpc() { | |||
@Override | |||
public void select(String rowKey) { | |||
SingleSelectionModel.this.select(getItemId(rowKey), false); | |||
} | |||
}); | |||
} | |||
@Override | |||
public boolean select(final Object itemId) { | |||
return select(itemId, true); | |||
} | |||
protected boolean select(final Object itemId, boolean refresh) { | |||
if (itemId == null) { | |||
return deselect(getSelectedRow()); | |||
} | |||
@@ -1066,7 +1102,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
if (modified) { | |||
final Collection<Object> deselected; | |||
if (selectedRow != null) { | |||
deselectInternal(selectedRow, false); | |||
deselectInternal(selectedRow, false, true); | |||
deselected = Collections.singleton(selectedRow); | |||
} else { | |||
deselected = Collections.emptySet(); | |||
@@ -1075,19 +1111,28 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
fireSelectionEvent(deselected, selection); | |||
} | |||
if (refresh) { | |||
refreshRow(itemId); | |||
} | |||
return modified; | |||
} | |||
private boolean deselect(final Object itemId) { | |||
return deselectInternal(itemId, true); | |||
return deselectInternal(itemId, true, true); | |||
} | |||
private boolean deselectInternal(final Object itemId, | |||
boolean fireEventIfNeeded) { | |||
boolean fireEventIfNeeded, boolean refresh) { | |||
final boolean modified = selection.remove(itemId); | |||
if (fireEventIfNeeded && modified) { | |||
fireSelectionEvent(Collections.singleton(itemId), | |||
Collections.emptySet()); | |||
if (modified) { | |||
if (refresh) { | |||
refreshRow(itemId); | |||
} | |||
if (fireEventIfNeeded) { | |||
fireSelectionEvent(Collections.singleton(itemId), | |||
Collections.emptySet()); | |||
} | |||
} | |||
return modified; | |||
} | |||
@@ -1113,23 +1158,25 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
@Override | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
grid.getState().singleSelectDeselectAllowed = deselectAllowed; | |||
getState().deselectAllowed = deselectAllowed; | |||
} | |||
@Override | |||
public boolean isDeselectAllowed() { | |||
return grid.getState(false).singleSelectDeselectAllowed; | |||
return getState().deselectAllowed; | |||
} | |||
@Override | |||
protected SingleSelectionModelState getState() { | |||
return (SingleSelectionModelState) super.getState(); | |||
} | |||
} | |||
/** | |||
* A default implementation for a {@link SelectionModel.None} | |||
*/ | |||
public static class NoSelectionModel implements SelectionModel.None { | |||
@Override | |||
public void setGrid(final Grid grid) { | |||
// NOOP, not needed for anything | |||
} | |||
public static class NoSelectionModel extends AbstractSelectionModel | |||
implements SelectionModel.None { | |||
@Override | |||
public boolean isSelected(final Object itemId) { | |||
@@ -1167,7 +1214,40 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
private int selectionLimit = DEFAULT_MAX_SELECTIONS; | |||
private boolean allSelected; | |||
@Override | |||
protected void extend(AbstractClientConnector target) { | |||
super.extend(target); | |||
registerRpc(new MultiSelectionModelServerRpc() { | |||
@Override | |||
public void select(List<String> rowKeys) { | |||
List<Object> items = new ArrayList<Object>(); | |||
for (String rowKey : rowKeys) { | |||
items.add(getItemId(rowKey)); | |||
} | |||
MultiSelectionModel.this.select(items, false); | |||
} | |||
@Override | |||
public void deselect(List<String> rowKeys) { | |||
List<Object> items = new ArrayList<Object>(); | |||
for (String rowKey : rowKeys) { | |||
items.add(getItemId(rowKey)); | |||
} | |||
MultiSelectionModel.this.deselect(items, false); | |||
} | |||
@Override | |||
public void selectAll() { | |||
MultiSelectionModel.this.selectAll(false); | |||
} | |||
@Override | |||
public void deselectAll() { | |||
MultiSelectionModel.this.deselectAll(false); | |||
} | |||
}); | |||
} | |||
@Override | |||
public boolean select(final Object... itemIds) | |||
@@ -1190,6 +1270,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
@Override | |||
public boolean select(final Collection<?> itemIds) | |||
throws IllegalArgumentException { | |||
return select(itemIds, true); | |||
} | |||
protected boolean select(final Collection<?> itemIds, boolean refresh) { | |||
if (itemIds == null) { | |||
throw new IllegalArgumentException("itemIds may not be null"); | |||
} | |||
@@ -1217,6 +1301,12 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
updateAllSelectedState(); | |||
if (refresh) { | |||
for (Object itemId : itemIds) { | |||
refreshRow(itemId); | |||
} | |||
} | |||
return selectionWillChange; | |||
} | |||
@@ -1270,6 +1360,10 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
@Override | |||
public boolean deselect(final Collection<?> itemIds) | |||
throws IllegalArgumentException { | |||
return deselect(itemIds, true); | |||
} | |||
protected boolean deselect(final Collection<?> itemIds, boolean refresh) { | |||
if (itemIds == null) { | |||
throw new IllegalArgumentException("itemIds may not be null"); | |||
} | |||
@@ -1285,15 +1379,25 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
updateAllSelectedState(); | |||
if (refresh) { | |||
for (Object itemId : itemIds) { | |||
refreshRow(itemId); | |||
} | |||
} | |||
return hasCommonElements; | |||
} | |||
@Override | |||
public boolean selectAll() { | |||
return selectAll(true); | |||
} | |||
protected boolean selectAll(boolean refresh) { | |||
// select will fire the event | |||
final Indexed container = grid.getContainerDataSource(); | |||
final Indexed container = getParentGrid().getContainerDataSource(); | |||
if (container != null) { | |||
return select(container.getItemIds()); | |||
return select(container.getItemIds(), refresh); | |||
} else if (selection.isEmpty()) { | |||
return false; | |||
} else { | |||
@@ -1302,14 +1406,18 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
* but I guess the only theoretically correct course of | |||
* action... | |||
*/ | |||
return deselectAll(); | |||
return deselectAll(false); | |||
} | |||
} | |||
@Override | |||
public boolean deselectAll() { | |||
return deselectAll(true); | |||
} | |||
protected boolean deselectAll(boolean refresh) { | |||
// deselect will fire the event | |||
return deselect(getSelectedRows()); | |||
return deselect(getSelectedRows(), refresh); | |||
} | |||
/** | |||
@@ -1382,11 +1490,15 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
} | |||
private void updateAllSelectedState() { | |||
if (allSelected != selection.size() >= selectionLimit) { | |||
allSelected = selection.size() >= selectionLimit; | |||
grid.getRpcProxy(GridClientRpc.class).setSelectAll(allSelected); | |||
if (getState().allSelected != selection.size() >= selectionLimit) { | |||
getState().allSelected = selection.size() >= selectionLimit; | |||
} | |||
} | |||
@Override | |||
protected MultiSelectionModelState getState() { | |||
return (MultiSelectionModelState) super.getState(); | |||
} | |||
} | |||
/** | |||
@@ -1571,7 +1683,7 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
* Grid rows. If a description is generated for a row, it is used for all | |||
* the cells in the row for which a {@link CellDescriptionGenerator cell | |||
* description} is not generated. | |||
* | |||
* | |||
* @see Grid#setRowDescriptionGenerator(CellDescriptionGenerator) | |||
* | |||
* @since 7.6 | |||
@@ -3783,6 +3895,17 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
+ " instead"); | |||
} | |||
} | |||
/** | |||
* Resends the row data for given item id to the client. | |||
* | |||
* @since | |||
* @param itemId | |||
* row to refresh | |||
*/ | |||
protected void refreshRow(Object itemId) { | |||
getParentGrid().datasourceExtension.updateRowData(itemId); | |||
} | |||
} | |||
/** | |||
@@ -3982,116 +4105,9 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
*/ | |||
private void initGrid() { | |||
setSelectionMode(getDefaultSelectionMode()); | |||
addSelectionListener(new SelectionListener() { | |||
@Override | |||
public void select(SelectionEvent event) { | |||
if (applyingSelectionFromClient) { | |||
/* | |||
* Avoid sending changes back to the client if they | |||
* originated from the client. Instead, the RPC handler is | |||
* responsible for keeping track of the resulting selection | |||
* state and notifying the client if it doens't match the | |||
* expectation. | |||
*/ | |||
return; | |||
} | |||
/* | |||
* The rows are pinned here to ensure that the client gets the | |||
* correct key from server when the selected row is first | |||
* loaded. | |||
* | |||
* Once the client has gotten info that it is supposed to select | |||
* a row, it will pin the data from the client side as well and | |||
* it will be unpinned once it gets deselected. Nothing on the | |||
* server side should ever unpin anything from KeyMapper. | |||
* Pinning is mostly a client feature and is only used when | |||
* selecting something from the server side. | |||
*/ | |||
for (Object addedItemId : event.getAdded()) { | |||
if (!getKeyMapper().isPinned(addedItemId)) { | |||
getKeyMapper().pin(addedItemId); | |||
} | |||
} | |||
getState().selectedKeys = getKeyMapper().getKeys( | |||
getSelectedRows()); | |||
} | |||
}); | |||
registerRpc(new GridServerRpc() { | |||
@Override | |||
public void select(List<String> selection) { | |||
Collection<Object> receivedSelection = getKeyMapper() | |||
.getItemIds(selection); | |||
applyingSelectionFromClient = true; | |||
try { | |||
SelectionModel selectionModel = getSelectionModel(); | |||
if (selectionModel instanceof SelectionModel.Single | |||
&& selection.size() <= 1) { | |||
Object select = null; | |||
if (selection.size() == 1) { | |||
select = getKeyMapper().getItemId(selection.get(0)); | |||
} | |||
((SelectionModel.Single) selectionModel).select(select); | |||
} else if (selectionModel instanceof SelectionModel.Multi) { | |||
((SelectionModel.Multi) selectionModel) | |||
.setSelected(receivedSelection); | |||
} else { | |||
throw new IllegalStateException("SelectionModel " | |||
+ selectionModel.getClass().getSimpleName() | |||
+ " does not support selecting the given " | |||
+ selection.size() + " items."); | |||
} | |||
} finally { | |||
applyingSelectionFromClient = false; | |||
} | |||
Collection<Object> actualSelection = getSelectedRows(); | |||
// Make sure all selected rows are pinned | |||
for (Object itemId : actualSelection) { | |||
if (!getKeyMapper().isPinned(itemId)) { | |||
getKeyMapper().pin(itemId); | |||
} | |||
} | |||
// Don't mark as dirty since this might be the expected state | |||
getState(false).selectedKeys = getKeyMapper().getKeys( | |||
actualSelection); | |||
JsonObject diffState = getUI().getConnectorTracker() | |||
.getDiffState(Grid.this); | |||
final String diffstateKey = "selectedKeys"; | |||
assert diffState.hasKey(diffstateKey) : "Field name has changed"; | |||
if (receivedSelection.equals(actualSelection)) { | |||
/* | |||
* We ended up with the same selection state that the client | |||
* sent us. There's nothing to send back to the client, just | |||
* update the diffstate so subsequent changes will be | |||
* detected. | |||
*/ | |||
JsonArray diffSelected = Json.createArray(); | |||
for (String rowKey : getState(false).selectedKeys) { | |||
diffSelected.set(diffSelected.length(), rowKey); | |||
} | |||
diffState.put(diffstateKey, diffSelected); | |||
} else { | |||
/* | |||
* Actual selection is not what the client expects. Make | |||
* sure the client gets a state change event by clearing the | |||
* diffstate and marking as dirty | |||
*/ | |||
diffState.remove(diffstateKey); | |||
markAsDirty(); | |||
} | |||
} | |||
@Override | |||
public void sort(String[] columnIds, SortDirection[] directions, | |||
boolean userOriginated) { | |||
@@ -4120,13 +4136,6 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
} | |||
} | |||
@Override | |||
public void selectAll() { | |||
assert getSelectionModel() instanceof SelectionModel.Multi : "Not a multi selection model!"; | |||
((SelectionModel.Multi) getSelectionModel()).selectAll(); | |||
} | |||
@Override | |||
public void itemClick(String rowKey, String columnId, | |||
MouseEventDetails details) { | |||
@@ -5019,25 +5028,11 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, | |||
if (this.selectionModel != selectionModel) { | |||
// this.selectionModel is null on init | |||
if (this.selectionModel != null) { | |||
this.selectionModel.reset(); | |||
this.selectionModel.setGrid(null); | |||
this.selectionModel.remove(); | |||
} | |||
this.selectionModel = selectionModel; | |||
this.selectionModel.setGrid(this); | |||
this.selectionModel.reset(); | |||
if (selectionModel.getClass().equals(SingleSelectionModel.class)) { | |||
getState().selectionMode = SharedSelectionMode.SINGLE; | |||
} else if (selectionModel.getClass().equals( | |||
MultiSelectionModel.class)) { | |||
getState().selectionMode = SharedSelectionMode.MULTI; | |||
} else if (selectionModel.getClass().equals(NoSelectionModel.class)) { | |||
getState().selectionMode = SharedSelectionMode.NONE; | |||
} else { | |||
throw new UnsupportedOperationException("Grid currently " | |||
+ "supports only its own bundled selection models"); | |||
} | |||
selectionModel.setGrid(this); | |||
} | |||
} | |||
@@ -20,7 +20,6 @@ import com.vaadin.shared.annotations.NoLayout; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
import elemental.json.JsonArray; | |||
import elemental.json.JsonObject; | |||
/** | |||
* RPC interface used for pushing container data to the client. | |||
@@ -94,13 +93,14 @@ public interface DataProviderRpc extends ClientRpc { | |||
public void resetDataAndSize(int size); | |||
/** | |||
* Informs the client that a row has updated. The client-side DataSource | |||
* will map the given data to correct index if it should be in the cache. | |||
* Informs the client that rows have been updated. The client-side | |||
* DataSource will map the given data to correct index if it should be in | |||
* the cache. | |||
* | |||
* @since 7.6 | |||
* @param row | |||
* the updated row data | |||
* @param rowArray | |||
* array of updated rows | |||
*/ | |||
@NoLayout | |||
public void updateRowData(JsonObject row); | |||
public void updateRowData(JsonArray rowArray); | |||
} |
@@ -55,15 +55,4 @@ public interface GridClientRpc extends ClientRpc { | |||
* Command client Grid to recalculate column widths. | |||
*/ | |||
public void recalculateColumnWidths(); | |||
/** | |||
* Informs the Grid that all items have been selected or not selected on the | |||
* server side. | |||
* | |||
* @since 7.5.3 | |||
* @param allSelected | |||
* <code>true</code> to check the select all checkbox, | |||
* <code>false</code> to uncheck it. | |||
*/ | |||
public void setSelectAll(boolean allSelected); | |||
} |
@@ -29,10 +29,6 @@ import com.vaadin.shared.data.sort.SortDirection; | |||
*/ | |||
public interface GridServerRpc extends ServerRpc { | |||
void select(List<String> newSelection); | |||
void selectAll(); | |||
void sort(String[] columnIds, SortDirection[] directions, | |||
boolean userOriginated); | |||
@@ -128,6 +128,13 @@ public class GridState extends TabIndexState { | |||
* */ | |||
public static final String JSONKEY_DETAILS_VISIBLE = "dv"; | |||
/** | |||
* The key that tells whether row is selected. | |||
* | |||
* @since | |||
*/ | |||
public static final String JSONKEY_SELECTED = "s"; | |||
/** | |||
* Columns in grid. | |||
*/ | |||
@@ -153,14 +160,6 @@ public class GridState extends TabIndexState { | |||
@DelegateToWidget | |||
public HeightMode heightMode = HeightMode.CSS; | |||
// instantiated just to avoid NPEs | |||
public List<String> selectedKeys = new ArrayList<String>(); | |||
public SharedSelectionMode selectionMode; | |||
/** Whether single select mode can be cleared through the UI */ | |||
public boolean singleSelectDeselectAllowed = true; | |||
/** Keys of the currently sorted columns */ | |||
public String[] sortColumns = new String[0]; | |||
@@ -0,0 +1,55 @@ | |||
/* | |||
* Copyright 2000-2014 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.shared.ui.grid.selection; | |||
import java.util.List; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
/** | |||
* ServerRpc for MultiSelectionModel. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface MultiSelectionModelServerRpc extends ServerRpc { | |||
/** | |||
* Select a list of rows based on their row keys on the server-side. | |||
* | |||
* @param rowKeys | |||
* list of row keys to select | |||
*/ | |||
public void select(List<String> rowKeys); | |||
/** | |||
* Deselect a list of rows based on their row keys on the server-side. | |||
* | |||
* @param rowKeys | |||
* list of row keys to deselect | |||
*/ | |||
public void deselect(List<String> rowKeys); | |||
/** | |||
* Selects all rows on the server-side. | |||
*/ | |||
public void selectAll(); | |||
/** | |||
* Deselects all rows on the server-side. | |||
*/ | |||
public void deselectAll(); | |||
} |
@@ -0,0 +1,31 @@ | |||
/* | |||
* Copyright 2000-2014 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.shared.ui.grid.selection; | |||
import com.vaadin.shared.communication.SharedState; | |||
/** | |||
* SharedState object for MultiSelectionModel. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public class MultiSelectionModelState extends SharedState { | |||
/* Select All -checkbox status */ | |||
public boolean allSelected; | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* Copyright 2000-2014 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.shared.ui.grid.selection; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
/** | |||
* ServerRpc for SingleSelectionModel. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface SingleSelectionModelServerRpc extends ServerRpc { | |||
/** | |||
* Selects a row on server-side. | |||
* | |||
* @param rowKey | |||
* row key of selected row; {@code null} if deselect | |||
*/ | |||
public void select(String rowKey); | |||
} |
@@ -0,0 +1,30 @@ | |||
/* | |||
* Copyright 2000-2014 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.shared.ui.grid.selection; | |||
import com.vaadin.shared.communication.SharedState; | |||
/** | |||
* SharedState object for SingleSelectionModel. | |||
* | |||
* @since | |||
* @author Vaadin Ltd | |||
*/ | |||
public class SingleSelectionModelState extends SharedState { | |||
/* Allow deselecting rows */ | |||
public boolean deselectAllowed; | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* Copyright 2000-2014 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.components.grid; | |||
import com.vaadin.annotations.Widgetset; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.tests.widgetset.TestingWidgetSet; | |||
import com.vaadin.ui.Grid.MultiSelectionModel; | |||
@Widgetset(TestingWidgetSet.NAME) | |||
public class GridCustomSelectionModel extends AbstractTestUI { | |||
public static class MySelectionModel extends MultiSelectionModel { | |||
} | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
PersonTestGrid grid = new PersonTestGrid(500); | |||
grid.setSelectionModel(new MySelectionModel()); | |||
addComponent(grid); | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
/* | |||
* Copyright 2000-2014 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.components.grid; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import org.openqa.selenium.By; | |||
import org.openqa.selenium.Keys; | |||
import com.vaadin.testbench.elements.GridElement; | |||
import com.vaadin.testbench.elements.GridElement.GridCellElement; | |||
import com.vaadin.testbench.parallel.TestCategory; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
@TestCategory("grid") | |||
public class GridCustomSelectionModelTest extends MultiBrowserTest { | |||
@Test | |||
public void testCustomSelectionModel() { | |||
setDebug(true); | |||
openTestURL(); | |||
GridElement grid = $(GridElement.class).first(); | |||
GridCellElement cell = grid.getCell(0, 0); | |||
assertTrue("First column of Grid should not have an input element", | |||
cell.findElements(By.className("input")).isEmpty()); | |||
assertFalse("Row should not be selected initially", grid.getRow(0) | |||
.isSelected()); | |||
cell.click(5, 5); | |||
assertTrue("Click should select row", grid.getRow(0).isSelected()); | |||
cell.click(5, 5); | |||
assertFalse("Click should deselect row", grid.getRow(0).isSelected()); | |||
grid.sendKeys(Keys.SPACE); | |||
assertTrue("Space should select row", grid.getRow(0).isSelected()); | |||
grid.sendKeys(Keys.SPACE); | |||
assertFalse("Space should deselect row", grid.getRow(0).isSelected()); | |||
assertNoErrorNotifications(); | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
/* | |||
* Copyright 2000-2014 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.widgetset.client.grid; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.connectors.MultiSelectionModelConnector; | |||
import com.vaadin.client.renderers.ComplexRenderer; | |||
import com.vaadin.client.widget.grid.selection.ClickSelectHandler; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.tests.components.grid.GridCustomSelectionModel.MySelectionModel; | |||
import elemental.json.JsonObject; | |||
@Connect(MySelectionModel.class) | |||
public class MySelectionModelConnector extends MultiSelectionModelConnector { | |||
private ClickSelectHandler<JsonObject> handler; | |||
@Override | |||
protected void extend(ServerConnector target) { | |||
super.extend(target); | |||
handler = new ClickSelectHandler<JsonObject>(getGrid()); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
super.onUnregister(); | |||
handler.removeHandler(); | |||
handler = null; | |||
} | |||
@Override | |||
protected Multi<JsonObject> createSelectionModel() { | |||
return new MySelectionModel(); | |||
} | |||
public class MySelectionModel extends MultiSelectionModel { | |||
@Override | |||
protected ComplexRenderer<Boolean> createSelectionColumnRenderer( | |||
Grid<JsonObject> grid) { | |||
// No Selection Column. | |||
return null; | |||
} | |||
} | |||
} |