Created
November 2, 2018 17:32
-
-
Save branflake2267/c7b83a04d4e1508be75d95fa1c7b07ac to your computer and use it in GitHub Desktop.
4.0.4-SNAPSHOT grid selection model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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