}
}
- private boolean isPinned() {
+ protected boolean isPinned() {
return pinnedRows.containsKey(key);
}
@Override
public void pin() {
- Integer count = pinnedCounts.get(key);
- if (count == null) {
- count = Integer.valueOf(0);
- pinnedRows.put(key, this);
- }
- pinnedCounts.put(key, Integer.valueOf(count.intValue() + 1));
+ pinHandle(this);
}
@Override
public void unpin() throws IllegalStateException {
- final Integer count = pinnedCounts.get(key);
- if (count == null) {
- throw new IllegalStateException("Row " + row + " with key "
- + key + " was not pinned to begin with");
- } else if (count.equals(Integer.valueOf(1))) {
- pinnedRows.remove(key);
- pinnedCounts.remove(key);
- } else {
- pinnedCounts.put(key, Integer.valueOf(count.intValue() - 1));
- }
+ unpinHandle(this);
}
@Override
}
}
+ /**
+ * Pins a row with given handle. This function can be overridden to do
+ * specific logic related to pinning rows.
+ *
+ * @param handle
+ * row handle to pin
+ */
+ protected void pinHandle(RowHandleImpl handle) {
+ Object key = handle.key;
+ Integer count = pinnedCounts.get(key);
+ if (count == null) {
+ count = Integer.valueOf(0);
+ pinnedRows.put(key, handle);
+ }
+ pinnedCounts.put(key, Integer.valueOf(count.intValue() + 1));
+ }
+
+ /**
+ * Unpins a previously pinned row with given handle. This function can be
+ * overridden to do specific logic related to unpinning rows.
+ *
+ * @param handle
+ * row handle to unpin
+ *
+ * @throws IllegalStateException
+ * if given row handle has not been pinned before
+ */
+ protected void unpinHandle(RowHandleImpl handle)
+ throws IllegalStateException {
+ Object key = handle.key;
+ final Integer count = pinnedCounts.get(key);
+ if (count == null) {
+ throw new IllegalStateException("Row " + handle.getRow()
+ + " with key " + key + " was not pinned to begin with");
+ } else if (count.equals(Integer.valueOf(1))) {
+ pinnedRows.remove(key);
+ pinnedCounts.remove(key);
+ } else {
+ pinnedCounts.put(key, Integer.valueOf(count.intValue() - 1));
+ }
+ }
+
@Override
public void ensureAvailability(int firstRowIndex, int numberOfRows) {
requestedAvailability = Range.withLength(firstRowIndex, numberOfRows);
*/
abstract public Object getRowKey(T row);
- /**
- * Marks rows as pinned when fetching new rows.
- * <p>
- * This collection of rows are intended to remain pinned if new rows are
- * fetched from the data source, even if some of the pinned rows would fall
- * off the cache and become inactive.
- * <p>
- * This method does nothing by itself, other than it stores the rows into a
- * field. The implementation needs to make all the adjustments for itself.
- * Check {@link RpcDataSourceConnector.RpcDataSource#requestRows(int, int)}
- * for an implementation example.
- *
- * @param keys
- * a collection of rows to keep pinned
- *
- * @see #temporarilyPinnedRows
- * @see RpcDataSourceConnector.RpcDataSource#requestRows(int, int)
- * @deprecated You probably don't want to call this method unless you're
- * writing a Renderer for a selection model. Even if you are, be
- * very aware what this method does and how it behaves.
- */
- @Deprecated
- public void transactionPin(Collection<T> rows) {
- if (rows == null) {
- throw new IllegalArgumentException("argument may not be null");
- }
- temporarilyPinnedRows = rows;
- }
-
protected void resetDataAndSize(int newSize) {
dropFromCache(getCachedRange());
cached = Range.withLength(0, 0);
package com.vaadin.client.data;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
public class RpcDataSource extends AbstractRemoteDataSource<JSONObject> {
- private Collection<JSONObject> prevRows = Collections.emptySet();
+ private DataRequestRpc rpcProxy = getRpcProxy(DataRequestRpc.class);
@Override
protected void requestRows(int firstRowIndex, int numberOfRows) {
Range cached = getCachedRange();
- Collection<JSONObject> newRows = new ArrayList<JSONObject>(
- temporarilyPinnedRows);
- newRows.removeAll(prevRows);
-
- List<String> temporarilyPinnedKeys = new ArrayList<String>(
- newRows.size());
- for (JSONObject row : newRows) {
- temporarilyPinnedKeys.add((String) getRowKey(row));
- }
-
- getRpcProxy(DataRequestRpc.class).requestRows(firstRowIndex,
- numberOfRows, cached.getStart(), cached.length(),
- temporarilyPinnedKeys);
-
- prevRows = temporarilyPinnedRows;
+ rpcProxy.requestRows(firstRowIndex, numberOfRows,
+ cached.getStart(), cached.length());
}
@Override
- public Object getRowKey(JSONObject row) {
+ public String getRowKey(JSONObject row) {
JSONString string = row.get(GridState.JSONKEY_ROWKEY).isString();
if (string != null) {
return string.stringValue();
}
@Override
- @SuppressWarnings("deprecation")
- public void transactionPin(Collection<JSONObject> keys) {
- super.transactionPin(keys);
- if (keys.isEmpty() && !prevRows.isEmpty()) {
- prevRows = Collections.emptySet();
- getRpcProxy(DataRequestRpc.class)
- .releaseTemporarilyPinnedKeys();
+ public int size() {
+ return getState().containerSize;
+ }
+
+ @Override
+ protected void pinHandle(RowHandleImpl handle) {
+ // Server only knows if something is pinned or not. No need to pin
+ // multiple times.
+ boolean pinnedBefore = handle.isPinned();
+ super.pinHandle(handle);
+ if (!pinnedBefore) {
+ rpcProxy.setPinned(getRowKey(handle.getRow()), true);
}
}
@Override
- public int size() {
- return getState().containerSize;
+ protected void unpinHandle(RowHandleImpl handle) {
+ // Row data is no longer available after it has been unpinned.
+ String key = getRowKey(handle.getRow());
+ super.unpinHandle(handle);
+ if (!handle.isPinned()) {
+ rpcProxy.setPinned(key, false);
+ }
+
}
}
package com.vaadin.client.ui.grid.selection;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.vaadin.client.Util;
-import com.vaadin.client.data.AbstractRemoteDataSource;
-import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.grid.Cell;
import com.vaadin.client.ui.grid.DataAvailableEvent;
import com.vaadin.client.ui.grid.DataAvailableHandler;
private boolean scrollAreaShouldRebound = false;
- private AbstractRemoteDataSource<T> remoteDataSource = null;
- private Batched<T> batchedSelectionModel = null;
-
public AutoScrollerAndSelector(final int topBound,
final int bottomBound, final int gradientArea,
final boolean selectionPaint) {
grid.setScrollTop(grid.getScrollTop() + intPixelsToScroll);
}
- @SuppressWarnings("hiding")
int logicalRow = getLogicalRowIndex(Util.getElementFromPoint(pageX,
pageY));
if (logicalRow != -1 && logicalRow != this.logicalRow) {
this.logicalRow = logicalRow;
setSelected(logicalRow, selectionPaint);
-
- if (remoteDataSource != null && batchedSelectionModel != null) {
- Collection<T> pinneds = batchedSelectionModel
- .getSelectedRowsBatch();
- pinneds.addAll(batchedSelectionModel
- .getDeselectedRowsBatch());
- remoteDataSource.transactionPin(pinneds);
- }
}
reschedule();
scrollSpeed = ratio * SCROLL_TOP_SPEED_PX_SEC;
}
- @SuppressWarnings("deprecation")
public void start(int logicalRowIndex) {
running = true;
setSelected(logicalRowIndex, selectionPaint);
logicalRow = logicalRowIndex;
reschedule();
-
- DataSource<T> dataSource = grid.getDataSource();
- SelectionModel<T> selectionModel = grid.getSelectionModel();
- if (dataSource instanceof AbstractRemoteDataSource
- && selectionModel instanceof Batched) {
- this.remoteDataSource = (AbstractRemoteDataSource<T>) dataSource;
- this.batchedSelectionModel = (Batched<T>) selectionModel;
-
- Collection<T> pinneds = batchedSelectionModel
- .getSelectedRowsBatch();
- pinneds.addAll(batchedSelectionModel.getDeselectedRowsBatch());
- remoteDataSource.transactionPin(pinneds);
- }
}
@SuppressWarnings("deprecation")
public void stop() {
running = false;
- if (remoteDataSource != null) {
- // split into two lines because of Java generics not playing
- // nice.
- @SuppressWarnings("unchecked")
- Collection<T> emptySet = (Collection<T>) Collections.emptySet();
- remoteDataSource.transactionPin(emptySet);
- remoteDataSource = null;
- batchedSelectionModel = null;
- }
-
if (handle != null) {
handle.cancel();
handle = null;
@Override
protected boolean deselectByHandle(RowHandle<T> handle) {
if (selectedRows.remove(handle)) {
+
if (!isBeingBatchSelected()) {
handle.unpin();
} else {
selectionBatch.clear();
final Collection<T> removed = getDeselectedRowsBatch();
+
+ // unpin deselected rows
+ for (RowHandle<T> handle : deselectionBatch) {
+ handle.unpin();
+ }
deselectionBatch.clear();
grid.fireEvent(new SelectionChangeEvent<T>(grid, added, removed,
import com.vaadin.ui.components.grid.Grid;
import com.vaadin.ui.components.grid.GridColumn;
import com.vaadin.ui.components.grid.Renderer;
-import com.vaadin.ui.components.grid.selection.SelectionChangeEvent;
-import com.vaadin.ui.components.grid.selection.SelectionChangeListener;
import elemental.json.Json;
import elemental.json.JsonArray;
rpc = getRpcProxy(DataProviderRpc.class);
registerRpc(new DataRequestRpc() {
- private Collection<String> allTemporarilyPinnedKeys = new ArrayList<String>();
-
@Override
public void requestRows(int firstRow, int numberOfRows,
- int firstCachedRowIndex, int cacheSize,
- List<String> temporarilyPinnedKeys) {
-
- for (String key : temporarilyPinnedKeys) {
- Object itemId = keyMapper.getItemId(key);
- if (!keyMapper.isPinned(itemId)) {
- keyMapper.pin(itemId);
- }
- }
- allTemporarilyPinnedKeys.addAll(temporarilyPinnedKeys);
+ int firstCachedRowIndex, int cacheSize) {
Range active = Range.withLength(firstRow, numberOfRows);
if (cacheSize != 0) {
}
@Override
- public void releaseTemporarilyPinnedKeys() {
- /*
- * This needs to be done deferredly since the selection event
- * comes after this RPC call.
- */
-
- final SelectionChangeListener listener = new SelectionChangeListener() {
- @Override
- public void selectionChange(SelectionChangeEvent event) {
- for (String tempPinnedKey : allTemporarilyPinnedKeys) {
- /*
- * TODO: this could be moved into a field instead of
- * inline to reduce indentations.
- */
-
- /*
- * This works around the fact that when deselecting
- * and leaping through the cache, the client tries
- * to send a deselect event even though a row never
- * was selected. So, it tries to unpin something
- * that never was temporarily pinned.
- *
- * If the same thing would happen while selecting
- * (instead of deselecting), the row would be
- * pinned, not because of the temporary pinning, but
- * because it's selected.
- */
- if (!keyMapper.isPinned(tempPinnedKey)) {
- continue;
- }
-
- Object itemId = keyMapper.getItemId(tempPinnedKey);
- Integer index = keyMapper.indexToItemId.inverse()
- .get(itemId);
- if (!getGrid().isSelected(itemId)
- && !activeRowHandler.activeRange
- .contains(index.intValue())) {
- keyMapper.unpin(itemId);
- }
- }
- allTemporarilyPinnedKeys = new ArrayList<String>();
- getGrid().removeSelectionChangeListener(this);
- }
- };
-
- getGrid().addSelectionChangeListener(listener);
+ public void setPinned(String key, boolean isPinned) {
+ if (isPinned) {
+ keyMapper.pin(keyMapper.getItemId(key));
+ } else {
+ keyMapper.unpin(keyMapper.getItemId(key));
+ }
}
});
addSelectionChangeListener(new SelectionChangeListener() {
@Override
public void selectionChange(SelectionChangeEvent event) {
- for (Object removedItemId : event.getRemoved()) {
- getKeyMapper().unpin(removedItemId);
- }
+ /*
+ * This listener nor anything else in the server side should
+ * never unpin anything from KeyMapper. Pinning is mostly a
+ * client feature and is only used when selecting something from
+ * the server side. This is to ensure that client has the
+ * correct key from server when the selected row is first
+ * loaded.
+ *
+ * Once 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.
+ */
for (Object addedItemId : event.getAdded()) {
if (!getKeyMapper().isPinned(addedItemId)) {
package com.vaadin.shared.data;
-import java.util.List;
-
+import com.vaadin.shared.annotations.Delayed;
import com.vaadin.shared.communication.ServerRpc;
/**
* the index of the first cached row
* @param cacheSize
* the number of cached rows
- * @param temporarilyPinnedKeys
- * the keys that should remain pinned, even if some of these
- * would fall out of the cache range
*/
public void requestRows(int firstRowIndex, int numberOfRows,
- int firstCachedRowIndex, int cacheSize,
- List<String> temporarilyPinnedKeys);
+ int firstCachedRowIndex, int cacheSize);
/**
- * Informs the back-end that the temporarily pinned keys in
- * {@link #requestRows(int, int, int, int, List)} may be released.
+ * Informs the server that an item referenced with a key pinned status has
+ * changed. This is a delayed call that happens along with next rpc call to
+ * server.
+ *
+ * @param key
+ * key mapping to item
+ * @param isPinned
+ * pinned status of referenced item
*/
- public void releaseTemporarilyPinnedKeys();
+ @Delayed
+ public void setPinned(String key, boolean isPinned);
}