Skip to content

Instantly share code, notes, and snippets.

@branflake2267
Created November 2, 2018 17:32
Show Gist options
  • Save branflake2267/c7b83a04d4e1508be75d95fa1c7b07ac to your computer and use it in GitHub Desktop.
Save branflake2267/c7b83a04d4e1508be75d95fa1c7b07ac to your computer and use it in GitHub Desktop.
4.0.4-SNAPSHOT grid selection model
package com.sencha.gxt.widget.core.client.selection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.HasBeforeSelectionHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.Store;
import com.sencha.gxt.data.shared.TreeStore;
import com.sencha.gxt.data.shared.event.StoreAddEvent;
import com.sencha.gxt.data.shared.event.StoreAddEvent.StoreAddHandler;
import com.sencha.gxt.data.shared.event.StoreClearEvent;
import com.sencha.gxt.data.shared.event.StoreClearEvent.StoreClearHandler;
import com.sencha.gxt.data.shared.event.StoreRecordChangeEvent;
import com.sencha.gxt.data.shared.event.StoreRecordChangeEvent.StoreRecordChangeHandler;
import com.sencha.gxt.data.shared.event.StoreRemoveEvent;
import com.sencha.gxt.data.shared.event.StoreRemoveEvent.StoreRemoveHandler;
import com.sencha.gxt.data.shared.event.StoreUpdateEvent;
import com.sencha.gxt.data.shared.event.StoreUpdateEvent.StoreUpdateHandler;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.HasSelectionChangedHandlers;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;
/**
* Abstract base class for store based selection models.
*
* @param <M> the model type contained within the store
*/
public abstract class AbstractStoreSelectionModel<M> implements StoreSelectionModel<M>, HasBeforeSelectionHandlers<M>,
HasSelectionHandlers<M>, HasSelectionChangedHandlers<M> {
protected class Handler implements StoreAddHandler<M>, StoreRemoveHandler<M>, StoreClearHandler<M>,
StoreRecordChangeHandler<M>, StoreUpdateHandler<M> {
@Override
public void onAdd(StoreAddEvent<M> event) {
AbstractStoreSelectionModel.this.onAdd(event.getItems());
}
@Override
public void onClear(StoreClearEvent<M> event) {
AbstractStoreSelectionModel.this.onClear(event);
}
@Override
public void onRecordChange(final StoreRecordChangeEvent<M> event) {
// run defer to ensure the code runs after grid view refreshes row
Scheduler.get().scheduleFinally(new ScheduledCommand() {
@Override
public void execute() {
AbstractStoreSelectionModel.this.onRecordChange(event);
}
});
}
@Override
public void onRemove(StoreRemoveEvent<M> event) {
AbstractStoreSelectionModel.this.onRemove(event.getItem());
}
@Override
public void onUpdate(StoreUpdateEvent<M> event) {
final List<M> update = event.getItems();
// run defer to ensure the code runs after grid view refreshes row
Scheduler.get().scheduleFinally(new ScheduledCommand() {
@Override
public void execute() {
for (int i = 0; i < update.size(); i++) {
AbstractStoreSelectionModel.this.onUpdate(update.get(i));
}
}
});
}
}
protected M lastSelected;
protected boolean locked;
protected List<M> selected = new ArrayList<M>();
protected SelectionMode selectionMode = SelectionMode.MULTI;
protected Store<M> store;
protected Handler handler = new Handler();
protected HandlerManager handlerManager;
protected GroupingHandlerRegistration handlerRegistration = new GroupingHandlerRegistration();
protected M lastFocused;
/**
* If true the selection change event should be fired on click. When a selection change event is fired on mouse down
* we don't actually fire the event until click so that any active fields will be blurred BEFORE the handler is run
*/
protected boolean fireSelectionChangeOnClick;
/**
* Tracks if a mouse down event is currently being processed. When a selection change event is fired on mouse down we
* don't actually fire the event until click so that any active fields will be blurred BEFORE the handler is run.
*/
protected boolean mouseDown;
/**
* Process select on mouse down or mouse down.
* The default select processing will happen on mouse down.
* If mouse up is set to true, you could cancel a drag.
* @since 4.0.3
*/
protected boolean processSelectOnMouseUp = false;
@Override
public HandlerRegistration addBeforeSelectionHandler(BeforeSelectionHandler<M> handler) {
return ensureHandlers().addHandler(BeforeSelectionEvent.getType(), handler);
}
@Override
public HandlerRegistration addSelectionChangedHandler(SelectionChangedHandler<M> handler) {
return ensureHandlers().addHandler(SelectionChangedEvent.getType(), handler);
}
/**
* Adds a {@link SelectionChangedHandler} handler for {@link SelectionChangedEvent} events.
*
* @since 3.0.1 This event is fired asynchronously.
*
* @param handler the handler
* @return the registration for the event
*/
@Override
public HandlerRegistration addSelectionHandler(SelectionHandler<M> handler) {
return ensureHandlers().addHandler(SelectionEvent.getType(), handler);
}
@Override
public void bind(Store<M> store) {
deselectAll();
if (this.store != null) {
handlerRegistration.removeHandler();
}
this.store = store;
if (store != null) {
if (handlerRegistration == null) {
handlerRegistration = new GroupingHandlerRegistration();
}
handlerRegistration.add(store.addStoreAddHandler(handler));
handlerRegistration.add(store.addStoreRemoveHandler(handler));
handlerRegistration.add(store.addStoreClearHandler(handler));
handlerRegistration.add(store.addStoreUpdateHandler(handler));
handlerRegistration.add(store.addStoreRecordChangeHandler(handler));
}
}
@Override
public void deselect(int index) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore<M>) store;
M m = ls.get(index);
if (m != null) {
doDeselect(Collections.singletonList(m), false);
}
}
}
@Override
public void deselect(int start, int end) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore<M>) store;
List<M> list = new ArrayList<M>();
for (int i = start; i < end; i++) {
M m = ls.get(i);
if (m != null) {
list.add(m);
}
}
doDeselect(list, false);
}
}
@Override
public void deselect(List<M> items) {
doDeselect(items, false);
}
@Override
public void deselect(M... items) {
deselect(Arrays.asList(items));
}
@Override
public void deselect(M item) {
deselect(Collections.singletonList(item));
}
@Override
public void deselectAll() {
doDeselect(new ArrayList<M>(selected), false);
}
@Override
public void fireEvent(GwtEvent<?> event) {
if (handlerManager != null) {
handlerManager.fireEvent(event);
}
}
@Override
public M getSelectedItem() {
return lastSelected;
}
@Override
public List<M> getSelectedItems() {
return new ArrayList<M>(selected);
}
public List<M> getSelection() {
return getSelectedItems();
}
@Override
public SelectionMode getSelectionMode() {
return selectionMode;
}
/**
* Returns true if the selection model is locked.
*
* @return the locked state
*/
public boolean isLocked() {
return locked;
}
@Override
public boolean isSelected(M item) {
for (M m : selected) {
if (store.hasMatchingKey(item, m)) {
return true;
}
}
return false;
}
@Override
public void refresh() {
List<M> sel = new ArrayList<M>();
boolean change = false;
for (M m : selected) {
M storeModel = store.findModel(m);
if (storeModel != null) {
sel.add(storeModel);
}
}
if (sel.size() != selected.size()) {
change = true;
}
selected.clear();
lastSelected = null;
setLastFocused(null);
doSelect(sel, false, true);
if (change) {
fireSelectionChange();
}
}
@Override
public void select(boolean keepExisting, M... items) {
select(Arrays.asList(items), keepExisting);
}
@Override
public void select(int index, boolean keepExisting) {
select(index, index, keepExisting);
}
@Override
public void select(int start, int end, boolean keepExisting) {
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore<M>) store;
List<M> sel = new ArrayList<M>();
if (start <= end) {
for (int i = start; i <= end; i++) {
sel.add(ls.get(i));
}
} else {
for (int i = start; i >= end; i--) {
sel.add(ls.get(i));
}
}
doSelect(sel, keepExisting, false);
}
}
@Override
public void select(List<M> items, boolean keepExisting) {
doSelect(items, keepExisting, false);
}
@Override
public void select(M item, boolean keepExisting) {
select(Collections.singletonList(item), keepExisting);
}
@Override
public void selectAll() {
select(store.getAll(), false);
}
/**
* True to lock the selection model. When locked, all selection changes are disabled.
*
* @param locked true to lock
*/
public void setLocked(boolean locked) {
this.locked = locked;
}
public void setSelection(List<M> selection) {
select(selection, false);
}
@Override
public void setSelectionMode(SelectionMode selectionMode) {
this.selectionMode = selectionMode;
}
/**
* Process the select event on mouse down or mouse up. The default is mouse down.
* @param processSelectOnMouseUp sets onMouseUp or mouseDown
* @since 4.0.3
*/
public void setProcessSelectOnMouseUp(boolean processSelectOnMouseUp) {
this.processSelectOnMouseUp = processSelectOnMouseUp;
}
protected void doDeselect(List<M> models, boolean suppressEvent) {
if (locked) return;
boolean change = false;
for (M m : models) {
if (selected.remove(m)) {
if (lastSelected == m) {
lastSelected = selected.size() > 0 ? selected.get(selected.size() - 1) : null;
}
onSelectChange(m, false);
change = true;
}
}
if (!suppressEvent && change) {
fireSelectionChange();
}
}
protected void doMultiSelect(List<M> models, boolean keepExisting, boolean suppressEvent) {
if (locked) return;
boolean change = false;
if (!keepExisting && selected.size() > 0) {
change = true;
doDeselect(new ArrayList<M>(selected), true);
}
for (M m : models) {
boolean isSelected = isSelected(m);
if (!suppressEvent && !isSelected) {
BeforeSelectionEvent<M> evt = BeforeSelectionEvent.fire(this, m);
if (evt != null && evt.isCanceled()) {
continue;
}
}
change = true;
lastSelected = m;
selected.add(m);
setLastFocused(lastSelected);
if (!isSelected) {
onSelectChange(m, true);
if (!suppressEvent) {
SelectionEvent.fire(this, m);
}
}
}
if (change && !suppressEvent) {
fireSelectionChange();
}
}
protected void doSelect(List<M> models, boolean keepExisting, boolean suppressEvent) {
if (locked) return;
if (selectionMode == SelectionMode.SINGLE) {
M m = models.size() > 0 ? models.get(0) : null;
if (m != null) {
doSingleSelect(m, suppressEvent);
}
} else {
doMultiSelect(models, keepExisting, suppressEvent);
}
}
protected void doSingleSelect(M model, boolean suppressEvent) {
if (locked) return;
int index = -1;
if (store instanceof ListStore) {
ListStore<M> ls = (ListStore<M>) store;
index = ls.indexOf(model);
}
if (store instanceof TreeStore) {
TreeStore<M> ls = (TreeStore<M>) store;
index = ls.indexOf(model);
}
if (index == -1 || isSelected(model)) {
return;
} else {
if (!suppressEvent) {
BeforeSelectionEvent<M> evt = BeforeSelectionEvent.fire(this, model);
if (evt != null && evt.isCanceled()) {
return;
}
}
}
boolean change = false;
if (selected.size() > 0 && !isSelected(model)) {
doDeselect(Collections.singletonList(lastSelected), true);
change = true;
}
if (selected.size() == 0) {
change = true;
}
selected.add(model);
lastSelected = model;
onSelectChange(model, true);
setLastFocused(lastSelected);
if (!suppressEvent) {
SelectionEvent.fire(this, model);
}
if (change && !suppressEvent) {
fireSelectionChange();
}
}
protected HandlerManager ensureHandlers() {
if (handlerManager == null) {
handlerManager = new HandlerManager(this);
}
return handlerManager;
}
protected void fireSelectionChange() {
if (mouseDown) {
fireSelectionChangeOnClick = true;
} else {
fireEvent(new SelectionChangedEvent<M>(selected));
}
}
protected M getLastFocused() {
return lastFocused;
}
protected void onAdd(List<? extends M> models) {
}
protected void onClear(StoreClearEvent<M> event) {
int oldSize = selected.size();
selected.clear();
lastSelected = null;
setLastFocused(null);
if (oldSize > 0) fireSelectionChange();
}
protected void onLastFocusChanged(M oldFocused, M newFocused) {
}
protected void onRecordChange(StoreRecordChangeEvent<M> event) {
onUpdate(event.getRecord().getModel());
}
protected void onRemove(M model) {
if (locked) return;
if (selected.remove(model)) {
if (lastSelected == model) {
lastSelected = null;
}
if (getLastFocused() == model) {
setLastFocused(null);
}
fireSelectionChange();
}
}
protected abstract void onSelectChange(M model, boolean select);
protected void onUpdate(M model) {
if (locked) return;
for (int i = 0; i < selected.size(); i++) {
M m = selected.get(i);
if (store.hasMatchingKey(model, m)) {
if (m != model) {
selected.remove(m);
selected.add(i, model);
}
if (lastSelected == m) {
lastSelected = model;
}
break;
}
}
if (getLastFocused() != null && model != getLastFocused() && store.hasMatchingKey(model, getLastFocused())) {
lastFocused = model;
}
}
protected void setLastFocused(M lastFocused) {
M lF = this.lastFocused;
this.lastFocused = lastFocused;
onLastFocusChanged(lF, lastFocused);
}
}
package com.sencha.gxt.widget.core.client.grid;
import java.util.Collections;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Event;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.core.client.util.KeyNav;
import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.Store;
import com.sencha.gxt.data.shared.event.StoreRecordChangeEvent;
import com.sencha.gxt.widget.core.client.event.RefreshEvent;
import com.sencha.gxt.widget.core.client.event.RefreshEvent.RefreshHandler;
import com.sencha.gxt.widget.core.client.event.RowClickEvent;
import com.sencha.gxt.widget.core.client.event.RowClickEvent.RowClickHandler;
import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent;
import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent.RowMouseDownHandler;
import com.sencha.gxt.widget.core.client.event.RowMouseUpEvent;
import com.sencha.gxt.widget.core.client.event.RowMouseUpEvent.RowMouseUpHandler;
import com.sencha.gxt.widget.core.client.event.RowTapEvent;
import com.sencha.gxt.widget.core.client.event.RowTapEvent.RowTapHandler;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent.ViewReadyHandler;
import com.sencha.gxt.widget.core.client.event.XEvent;
import com.sencha.gxt.widget.core.client.grid.Grid.Callback;
import com.sencha.gxt.widget.core.client.grid.Grid.GridCell;
import com.sencha.gxt.widget.core.client.selection.AbstractStoreSelectionModel;
/**
* Grid selection model.
*
* <dl>
* <dt>Inherited Events:</dt>
* <dd>AbstractStoreSelectionModel BeforeSelect</dd>
* <dd>AbstractStoreSelectionModel SelectionChange</dd>
* </dl>
*/
public class GridSelectionModel<M> extends AbstractStoreSelectionModel<M> {
/**
* Determines whether a given cell is selectable.
*/
public static class SelectionModelCallback implements Callback {
protected GridSelectionModel<?> sm;
/**
* Creates a selection model callback that determines whether a given cell is selectable.
*
* @param sm the selection model
*/
public SelectionModelCallback(GridSelectionModel<?> sm) {
this.sm = sm;
}
@Override
public boolean isSelectable(GridCell cell) {
return sm.isSelectable(cell.getRow(), cell.getCol());
}
}
protected class Handler implements RowMouseDownHandler, RowMouseUpHandler, RowClickHandler,
ViewReadyHandler, RefreshHandler, RowTapHandler {
@Override
public void onRefresh(RefreshEvent event) {
refresh();
if (getLastFocused() != null) {
grid.getView().onHighlightRow(listStore.indexOf(getLastFocused()), true);
}
}
@Override
public void onRowClick(RowClickEvent event) {
GridSelectionModel.this.onRowClick(event);
}
@Override
public void onRowMouseDown(RowMouseDownEvent event) {
GridSelectionModel.this.onRowMouseDown(event);
}
@Override
public void onRowMouseUp(RowMouseUpEvent event) {
GridSelectionModel.this.onRowMouseUp(event);
}
@Override
public void onViewReady(ViewReadyEvent event) {
refresh();
}
@Override
public void onRowTap(RowTapEvent event) {
GridSelectionModel.this.onRowTap(event);
}
}
/**
* True if this grid selection model supports keyboard navigation (defaults to true).
*/
protected boolean enableNavKeys = true;
/**
* The grid associated with this selection model.
*/
protected Grid<M> grid;
/**
* The current keyboard navigator.
*/
protected KeyNav keyNav = new KeyNav() {
@Override
public void onDown(NativeEvent e) {
GridSelectionModel.this.onKeyDown(e);
}
@Override
public void onKeyPress(NativeEvent ce) {
GridSelectionModel.this.onKeyPress(ce);
}
@Override
public void onLeft(NativeEvent e) {
GridSelectionModel.this.onKeyLeft(e);
}
@Override
public void onRight(NativeEvent e) {
GridSelectionModel.this.onKeyRight(e);
}
@Override
public void onUp(NativeEvent e) {
GridSelectionModel.this.onKeyUp(e);
}
};
/**
* The list store for this selection model.
*/
protected ListStore<M> listStore;
/**
* A group of handler registrations for this selection model.
*/
protected GroupingHandlerRegistration handlerRegistration;
/**
* Track the selection index, when the shift combined with and so then this is the starting point of the selection.
*/
protected int indexOnSelectNoShift;
/**
* True to deselect a selected item on click (defaults to true).
*/
protected boolean deselectOnSimpleClick = true;
protected boolean focusCellCalled;
protected Handler handler = new Handler();
@Override
public void bind(Store<M> store) {
super.bind(store);
if (store instanceof ListStore) {
listStore = (ListStore<M>) store;
} else {
listStore = null;
}
}
/**
* Binds the given grid to this selection model.
*
* @param grid the grid to bind to this selection model
*/
public void bindGrid(Grid<M> grid) {
if (this.grid != null) {
if (handlerRegistration != null) {
handlerRegistration.removeHandler();
handlerRegistration = null;
}
keyNav.bind(null);
bind(null);
}
this.grid = grid;
if (grid != null) {
if (handlerRegistration == null) {
handlerRegistration = new GroupingHandlerRegistration();
}
handlerRegistration.add(grid.addRowMouseDownHandler(handler));
handlerRegistration.add(grid.addRowMouseUpHandler(handler));
handlerRegistration.add(grid.addRowTapHandler(handler));
handlerRegistration.add(grid.addRowClickHandler(handler));
handlerRegistration.add(grid.addViewReadyHandler(handler));
handlerRegistration.add(grid.addRefreshHandler(handler));
keyNav.bind(grid);
bind(grid.getStore());
}
}
/**
* Returns the currently bound grid.
*
* @return the grid
*/
public Grid<M> getGrid() {
return grid;
}
/**
* Selects the next row.
*
* @param keepexisting true to keep existing selections
*/
public void selectNext(boolean keepexisting) {
if (hasNext()) {
int idx = listStore.indexOf(lastSelected) + 1;
select(idx, keepexisting);
grid.getView().focusRow(idx);
}
}
/**
* Selects the previous row.
*
* @param keepexisting true to keep existing selections
*/
public void selectPrevious(boolean keepexisting) {
if (hasPrevious()) {
int idx = listStore.indexOf(lastSelected) - 1;
select(idx, keepexisting);
grid.getView().focusRow(idx);
}
}
/**
* Handles a row click event. The row click event is responsible for adding to a selection in multiple selection mode.
*
* @param event the row click event
*/
protected void onRowClick(RowClickEvent event) {
if (fireSelectionChangeOnClick) {
fireSelectionChange();
fireSelectionChangeOnClick = false;
}
}
/**
* Handles a row mouse up event. The row mouse down event is responsible for initiating a selection.
*
* @param event the row mouse down event
* @since 4.0.3
*/
protected void onRowMouseUp(RowMouseUpEvent event) {
if (processSelectOnMouseUp) {
onRowSelect(event.getEvent(), event.getRowIndex(), event.getColumnIndex());
}
}
/**
* Handles a row mouse down event. The row mouse down event is responsible for initiating a selection.
*
* @param event the row mouse down event
*/
protected void onRowMouseDown(RowMouseDownEvent event) {
if (!processSelectOnMouseUp) {
onRowSelect(event.getEvent(), event.getRowIndex(), event.getColumnIndex());
}
}
/**
* @param event The row tap event.
* @since 4.0.3
*/
public void onRowTap(RowTapEvent event) {
if (fireSelectionChangeOnClick) {
fireSelectionChange();
fireSelectionChangeOnClick = false;
}
onRowSelect(event.getEvent(), event.getRowIndex(), event.getColumnIndex());
}
/**
* Handles a row mouse down event. The row mouse down event is responsible for initiating a selection.
*
* @param event the row mouse down event
* @since 4.0.3
*/
protected void onRowSelect(Event event, int rowIndex, int colIndex) {
if (Element.is(event.getEventTarget()) && !grid.getView().isSelectableTarget(Element.as(event.getEventTarget()))) {
return;
}
if (isLocked()) {
return;
}
if (rowIndex == -1) {
deselectAll();
return;
}
focusCellCalled = false;
mouseDown = true;
XEvent e = event.<XEvent>cast();
// It is important the focusCell be called once, and only once in onRowMouseDown and onRowMouseClick
// everything but multi select with the control key pressed is handled in mouse down
if (event.getButton() == Event.BUTTON_RIGHT) {
if (selectionMode != SelectionMode.SINGLE && isSelected(listStore.get(rowIndex))) {
onRightClick(event, rowIndex, colIndex);
return;
}
grid.getView().focusCell(rowIndex, colIndex, false);
select(rowIndex, false);
focusCellCalled = true;
} else {
M sel = listStore.get(rowIndex);
if (sel == null) {
return;
}
boolean isSelected = isSelected(sel);
boolean isMeta = e.getCtrlOrMetaKey();
boolean isShift = event.getShiftKey();
switch (selectionMode) {
case SIMPLE:
grid.getView().focusCell(rowIndex, colIndex, false);
focusCellCalled = true;
if (!isSelected) {
select(sel, true);
} else if (isSelected && deselectOnSimpleClick) {
deselect(sel);
}
break;
case SINGLE:
grid.getView().focusCell(rowIndex, colIndex, false);
focusCellCalled = true;
if (isSelected && isMeta) {
deselect(sel);
} else if (!isSelected) {
select(sel, false);
}
break;
case MULTI:
if (isSelected && isMeta) {
focusCellCalled = true;
// reset the starting location of the click
indexOnSelectNoShift = rowIndex;
doDeselect(Collections.singletonList(sel), false);
} else if (isMeta) {
focusCellCalled = true;
// reset the starting location of the click
indexOnSelectNoShift = rowIndex;
doSelect(Collections.singletonList(sel), true, false);
} else if (isSelected && !isMeta && !isShift && selected.size() > 1) {
// reset the starting location of the click
indexOnSelectNoShift = rowIndex;
doSelect(Collections.singletonList(sel), false, false);
} else if (isShift && lastSelected != null) {
int start;
int end;
// This deals with flipping directions
if (indexOnSelectNoShift < rowIndex) {
start = indexOnSelectNoShift;
end = rowIndex;
} else {
start = rowIndex;
end = indexOnSelectNoShift;
}
focusCellCalled = true;
select(start, end, false);
rowIndex = end; // focus on the last selected row
} else if (!isSelected) {
// reset the starting location of multi select
indexOnSelectNoShift = rowIndex;
focusCellCalled = true;
doSelect(Collections.singletonList(sel), false, false);
}
if (!focusCellCalled) {
grid.getView().focusCell(rowIndex, colIndex, false);
}
break;
}
}
mouseDown = false;
}
/**
* @since 4.0.3
*/
protected void onRightClick(Event event, int rowIndex, int colIndex) {
}
/**
* Returns true if there is an item in the list store following the most recently selected item (i.e. the selected
* item is not the last item in the list store).
*
* @return true if there is an item following the most recently selected item
*/
protected boolean hasNext() {
return lastSelected != null && listStore.indexOf(lastSelected) < (listStore.size() - 1);
}
/**
* Returns true if there is an item in the list store preceding the most recently selected item (i.e. the selected
* item is not the first item in the list store).
*
* @return true if there is an item preceding the most recently selected item
*/
protected boolean hasPrevious() {
return lastSelected != null && listStore.indexOf(lastSelected) > 0;
}
/**
* Returns true if the given cell is selectable.
*
* @param row the cell's row
* @param cell the cell's column
* @return true if the cell is selectable
*/
protected boolean isSelectable(int row, int cell) {
return !grid.getColumnModel().isHidden(cell);
}
/**
* Handles a key down event (e.g. as fired by the key navigator).
*
* @param nativeEvent the key down event
*/
protected void onKeyDown(NativeEvent nativeEvent) {
XEvent e = nativeEvent.<XEvent>cast();
if (Element.is(nativeEvent.getEventTarget())
&& !grid.getView().isSelectableTarget(Element.as(nativeEvent.getEventTarget()))) {
return;
}
if (listStore.size() == 0) {
return;
}
if (!e.getCtrlOrMetaKey() && selected.size() == 0 && getLastFocused() == null) {
select(0, false);
} else {
int idx = listStore.indexOf(getLastFocused());
if (idx >= 0 && (idx + 1) < listStore.size()) {
if (e.getCtrlOrMetaKey() || (e.getShiftKey() && isSelected(listStore.get(idx + 1)))) {
if (!e.getCtrlOrMetaKey()) {
deselect(idx);
}
M lF = listStore.get(idx + 1);
if (lF != null) {
setLastFocused(lF);
grid.getView().focusCell(idx + 1, -1, false);
}
} else {
if (e.getShiftKey() && lastSelected != getLastFocused()) {
grid.getView().focusCell(idx + 1, -1, false);
select(listStore.indexOf(lastSelected), idx + 1, true);
} else {
if (idx + 1 < listStore.size()) {
grid.getView().focusCell(idx + 1, -1, false);
selectNext(e.getShiftKey());
}
}
}
} else {
grid.getView().onNoNext(idx);
}
}
e.preventDefault();
}
/**
* Handles a key left event (e.g. as fired by the key navigator).
*
* @param e the key left event
*/
protected void onKeyLeft(NativeEvent e) {
}
/**
* Handles a key press event (e.g. as fired by the key navigator).
*
* @param nativeEvent the key press event
*/
protected void onKeyPress(NativeEvent nativeEvent) {
if (Element.is(nativeEvent.getEventTarget())
&& !grid.getView().isSelectableTarget(Element.as(nativeEvent.getEventTarget()))) {
return;
}
int keyCode = nativeEvent.getKeyCode();
XEvent e = nativeEvent.<XEvent>cast();
if (lastSelected != null && enableNavKeys) {
if (keyCode == KeyCodes.KEY_PAGEUP) {
e.stopPropagation();
e.preventDefault();
select(0, false);
grid.getView().focusRow(0);
} else if (keyCode == KeyCodes.KEY_PAGEDOWN) {
e.stopPropagation();
e.preventDefault();
int idx = listStore.indexOf(listStore.get(listStore.size() - 1));
select(idx, false);
grid.getView().focusRow(idx);
}
}
// if space bar is pressed
if (e.getKeyCode() == 32) {
if (getLastFocused() != null) {
if (e.getShiftKey() && lastSelected != null) {
int last = listStore.indexOf(lastSelected);
int i = listStore.indexOf(getLastFocused());
grid.getView().focusCell(i, -1, false);
select(last, i, e.getCtrlOrMetaKey());
} else {
if (isSelected(getLastFocused())) {
deselect(getLastFocused());
} else {
grid.getView().focusCell(listStore.indexOf(getLastFocused()), -1, false);
select(getLastFocused(), true);
}
}
}
}
}
/**
* Handles a key right event (e.g. as fired by the key navigator).
*
* @param e the key right event
*/
protected void onKeyRight(NativeEvent e) {
}
/**
* Handles a key up event (e.g. as fired by the key navigator).
*
* @param nativeEvent the key up event
*/
protected void onKeyUp(NativeEvent nativeEvent) {
XEvent xe = nativeEvent.<XEvent>cast();
if (Element.is(nativeEvent.getEventTarget())
&& !grid.getView().isSelectableTarget(Element.as(nativeEvent.getEventTarget()))) {
return;
}
int idx = listStore.indexOf(getLastFocused());
if (idx >= 1) {
if (xe.getCtrlOrMetaKey() || (nativeEvent.getShiftKey() && isSelected(listStore.get(idx - 1)))) {
if (!xe.getCtrlOrMetaKey()) {
deselect(idx);
}
M lF = listStore.get(idx - 1);
if (lF != null) {
setLastFocused(lF);
grid.getView().focusCell(idx - 1, -1, false);
}
} else {
if (nativeEvent.getShiftKey() && lastSelected != getLastFocused()) {
grid.getView().focusCell(idx - 1, -1, false);
select(listStore.indexOf(lastSelected), idx - 1, true);
} else {
if (idx > 0) {
grid.getView().focusCell(idx - 1, -1, false);
selectPrevious(nativeEvent.getShiftKey());
}
}
}
} else {
grid.getView().onNoPrev();
}
nativeEvent.preventDefault();
}
@Override
protected void onLastFocusChanged(M oldFocused, M newFocused) {
int i;
if (oldFocused != null) {
i = listStore.indexOf(oldFocused);
if (i >= 0) {
grid.getView().onHighlightRow(i, false);
}
}
if (newFocused != null) {
i = listStore.indexOf(newFocused);
if (i >= 0) {
grid.getView().onHighlightRow(i, true);
}
}
}
@Override
protected void onRecordChange(StoreRecordChangeEvent<M> event) {
super.onRecordChange(event);
refreshRowSelection(event.getRecord().getModel());
}
@Override
protected void onSelectChange(M model, boolean select) {
int idx = listStore.indexOf(model);
if (idx != -1) {
if (select) {
grid.getView().onRowSelect(idx);
} else {
grid.getView().onRowDeselect(idx);
}
}
}
@Override
protected void onUpdate(final M model) {
super.onUpdate(model);
refreshRowSelection(model);
}
protected void refreshRowSelection(final M model) {
Scheduler.get().scheduleFinally(new ScheduledCommand() {
@Override
public void execute() {
if (isSelected(model)) {
onSelectChange(model, true);
}
if (getLastFocused() == model) {
setLastFocused(getLastFocused());
}
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment