/*
* Ext GWT 2.2.4 - Ext for GWT
* Copyright(c) 2007-2010, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.widget.grid;
import java.util.Arrays;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.aria.FocusFrame;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ColumnModelEvent;
import com.extjs.gxt.ui.client.event.DomEvent;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.widget.grid.ColumnHeader.Head;
import com.extjs.gxt.ui.client.widget.selection.AbstractStoreSelectionModel;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Element;
/**
* Grid selection model.
*
* <dl>
* <dt>Inherited Events:</dt>
* <dd>AbstractStoreSelectionModel BeforeSelect</dd>
* <dd>AbstractStoreSelectionModel SelectionChange</dd>
* </dl>
*/
@SuppressWarnings("rawtypes")
public class GridSelectionModel<M extends ModelData> extends AbstractStoreSelectionModel<M> implements
Listener<BaseEvent> {
public static class Callback {
private GridSelectionModel sm;
public Callback(GridSelectionModel sm) {
this.sm = sm;
}
public boolean isSelectable(int row, int cell, boolean acceptsNav) {
return sm.isSelectable(row, cell, acceptsNav);
}
}
public static class Cell {
public int cell;
public int row;
public Cell(int row, int cell) {
this.row = row;
this.cell = cell;
}
}
protected boolean enableNavKeys = true;
protected Grid<M> grid;
protected boolean grouped = false;
protected GroupingView groupingView;
protected KeyNav<GridEvent<M>> keyNav = new KeyNav<GridEvent<M>>() {
@Override
public void onDown(GridEvent<M> e) {
onKeyDown(e);
}
@Override
public void onKeyPress(GridEvent<M> ce) {
GridSelectionModel.this.onKeyPress(ce);
}
@Override
public void onLeft(GridEvent<M> ce) {
onKeyLeft(ce);
}
@Override
public void onRight(GridEvent<M> ce) {
onKeyRight(ce);
}
@Override
public void onUp(GridEvent<M> e) {
onKeyUp(e);
}
};
protected ListStore<M> listStore;
protected Element selectedGroup;
protected Head selectedHeader;
private Callback callback = new Callback(this);
private boolean moveEditorOnEnter;
@SuppressWarnings("unchecked")
@Override
public void bind(Store store) {
super.bind(store);
if (store instanceof ListStore) {
listStore = (ListStore<M>) store;
} else {
listStore = null;
}
}
@SuppressWarnings("unchecked")
public void bindGrid(Grid grid) {
if (this.grid != null) {
this.grid.removeListener(Events.RowMouseDown, this);
this.grid.removeListener(Events.RowClick, this);
this.grid.removeListener(Events.ContextMenu, this);
this.grid.removeListener(Events.ViewReady, this);
this.grid.getView().removeListener(Events.RowUpdated, this);
this.grid.getView().removeListener(Events.Refresh, this);
this.grid.getColumnModel().removeListener(Events.HiddenChange, this);
keyNav.bind(null);
bind(null);
}
this.grid = grid;
if (grid != null) {
grid.addListener(Events.RowMouseDown, this);
grid.addListener(Events.RowClick, this);
grid.addListener(Events.ContextMenu, this);
grid.addListener(Events.ViewReady, this);
grid.getView().addListener(Events.RowUpdated, this);
grid.getView().addListener(Events.Refresh, this);
grid.getColumnModel().addListener(Events.HiddenChange, this);
keyNav.bind(grid);
bind(grid.getStore());
grouped = grid.getView() instanceof GroupingView;
if (grouped) groupingView = (GroupingView) grid.getView();
}
}
@SuppressWarnings("unchecked")
public void handleEvent(BaseEvent e) {
EventType type = e.getType();
if (type == Events.RowMouseDown) {
handleMouseDown((GridEvent) e);
} else if (type == Events.RowClick) {
handleMouseClick((GridEvent) e);
} else if (type == Events.RowUpdated) {
onRowUpdated((GridEvent) e);
} else if (type == Events.Refresh || type == Events.ViewReady) {
refresh();
if (getLastFocused() != null) {
grid.getView().onHighlightRow(listStore.indexOf(getLastFocused()), true);
}
} else if (type == Events.HiddenChange) {
handleColumnHidden((ColumnModelEvent) e);
}
}
/**
* Returns true of the editor moves on enter.
*
* @return true if editor moves on enter
*/
public boolean isMoveEditorOnEnter() {
return moveEditorOnEnter;
}
public void onEditorKey(DomEvent e) {
int k = e.getKeyCode();
Cell newCell = null;
CellEditor editor = grid.editSupport.getActiveEditor();
switch (k) {
case KeyCodes.KEY_ENTER:
case KeyCodes.KEY_TAB:
e.stopEvent();
if (editor != null) {
editor.completeEdit();
}
if ((k == KeyCodes.KEY_ENTER && moveEditorOnEnter) || k == KeyCodes.KEY_TAB) {
if (e.isShiftKey()) {
newCell = grid.walkCells(editor.row, editor.col - 1, -1, callback, true);
} else {
newCell = grid.walkCells(editor.row, editor.col + 1, 1, callback, true);
}
}
break;
case KeyCodes.KEY_ESCAPE:
if (editor != null) {
editor.cancelEdit();
}
break;
}
if (newCell != null) {
grid.editSupport.startEditing(newCell.row, newCell.cell);
} else {
if (k == KeyCodes.KEY_ENTER || k == KeyCodes.KEY_TAB || k == KeyCodes.KEY_ESCAPE) {
grid.getView().focusCell(editor.row, editor.col, false);
}
}
}
/**
* 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);
}
}
/**
* Set this to true to move the editor to the next editable cell on pressing
* enter.
*
* @param moveEditorOnEnter true to move the editor on pressing enter.
*/
public void setMoveEditorOnEnter(boolean moveEditorOnEnter) {
this.moveEditorOnEnter = moveEditorOnEnter;
}
protected void handleColumnHidden(ColumnModelEvent e) {
ColumnHeader header = grid.getView().getHeader();
if (header != null) {
int col = e.getColIndex();
Head h = header.getHead(col);
if (h == selectedHeader) {
selectedHeader = null;
}
}
}
@SuppressWarnings("unchecked")
protected void handleMouseClick(GridEvent<M> e) {
if (isLocked() || isInput(e.getTarget())) {
return;
}
if (e.getRowIndex() == -1) {
deselectAll();
return;
}
if (selectionMode == SelectionMode.MULTI) {
M sel = listStore.getAt(e.getRowIndex());
if (e.isControlKey() && isSelected(sel)) {
doDeselect(Arrays.asList(sel), false);
} else if (e.isControlKey()) {
doSelect(Arrays.asList(sel), true, false);
grid.getView().focusCell(e.getRowIndex(), e.getColIndex(), false);
} else if (isSelected(sel) && !e.isShiftKey() && !e.isControlKey() && selected.size() > 1) {
doSelect(Arrays.asList(sel), false, false);
grid.getView().focusCell(e.getRowIndex(), e.getColIndex(), false);
}
}
}
@SuppressWarnings("unchecked")
protected void handleMouseDown(GridEvent<M> e) {
if (e.getRowIndex() == -1 || isLocked() || isInput(e.getTarget())) {
return;
}
if (e.isRightClick()) {
if (selectionMode != SelectionMode.SINGLE && isSelected(listStore.getAt(e.getRowIndex()))) {
return;
}
select(e.getRowIndex(), false);
grid.getView().focusCell(e.getRowIndex(), e.getColIndex(), false);
} else {
M sel = listStore.getAt(e.getRowIndex());
if (selectionMode == SelectionMode.SIMPLE) {
if (!isSelected(sel)) {
select(sel, true);
grid.getView().focusCell(e.getRowIndex(), e.getColIndex(), false);
}
} else if (selectionMode == SelectionMode.SINGLE) {
if (e.isControlKey() && isSelected(sel)) {
deselect(sel);
} else if (!isSelected(sel)) {
select(sel, false);
grid.getView().focusCell(e.getRowIndex(), e.getColIndex(), false);
}
} else if (!e.isControlKey()) {
if (e.isShiftKey() && lastSelected != null) {
int last = listStore.indexOf(lastSelected);
int index = e.getRowIndex();
select(last, index, e.isControlKey());
grid.getView().focusCell(index, e.getColIndex(), false);
} else if (!isSelected(sel)) {
doSelect(Arrays.asList(sel), false, false);
grid.getView().focusCell(e.getRowIndex(), e.getColIndex(), false);
}
}
}
}
protected boolean hasNext() {
return lastSelected != null && listStore.indexOf(lastSelected) < (listStore.getCount() - 1);
}
protected boolean hasPrevious() {
return lastSelected != null && listStore.indexOf(lastSelected) > 0;
}
protected boolean isInput(Element target) {
String tag = target.getTagName();
return "input".equalsIgnoreCase(tag) || "textarea".equalsIgnoreCase(tag);
}
protected boolean isSelectable(int row, int cell, boolean acceptsNav) {
if (acceptsNav) {
return !grid.getColumnModel().isHidden(cell) && grid.getColumnModel().isCellEditable(cell);
} else {
return !grid.getColumnModel().isHidden(cell);
}
}
protected void onKeyDown(GridEvent<M> e) {
if (GXT.isFocusManagerEnabled()) {
if (selectedGroup == null && (selectedHeader != null || selected.size() == 0)) {
e.cancelBubble();
if (e.isAltKey()) {
grid.getView().getHeader().showColumnMenu(selectedHeader.column);
return;
}
if (selectedHeader != null) {
selectedHeader.deactivate();
}
select(0, false);
return;
}
if (grouped) {
GroupingView view = (GroupingView) grid.getView();
NodeList<Element> groups = view.getGroups().cast();
int gc = view.getGroups().getLength();
if (selectedGroup != null) {
int gindex = indexOf(groups, selectedGroup);
if (!view.isExpanded(selectedGroup)) {
if (gindex < gc - 1) {
view.onGroupSelect(selectedGroup, false);
selectedGroup = groups.getItem(gindex + 1);
view.onGroupSelect(selectedGroup, true);
}
return;
}
view.onGroupSelect(selectedGroup, false);
Element r = view.getGroupRow(selectedGroup, 0).cast();
selectedGroup = null;
if (r != null) {
int idx = view.findRowIndex(r);
select(idx, false);
view.focusRow(idx);
return;
}
}
if (lastSelected != null) {
Element row = view.getRow(lastSelected).cast();
Element group = view.findGroup(row).cast();
int totalGroups = groups.getLength();
int groupIndex = indexOf(groups, group);
NodeList<Element> groupRows = group.getChildNodes().getItem(1).getChildNodes().cast();
int rowsInGroup = group.getChildNodes().getItem(1).getChildNodes().getLength();
int rowInGroupIndex = indexOf(groupRows, row);
if (rowInGroupIndex == rowsInGroup - 1) {
if (groupIndex < totalGroups - 1) {
deselectAll();
selectedGroup = groups.getItem(groupIndex + 1);
view.onGroupSelect(selectedGroup, true);
return;
}
}
}
}
}
if (!e.isControlKey() && selected.size() == 0 && getLastFocused() == null) {
select(0, false);
} else {
int idx = listStore.indexOf(getLastFocused());
if (idx >= 0) {
if (e.isControlKey() || (e.isShiftKey() && isSelected(listStore.getAt(idx + 1)))) {
if (!e.isControlKey()) {
deselect(idx);
}
M lF = listStore.getAt(idx + 1);
if (lF != null) {
setLastFocused(lF);
grid.getView().focusCell(idx + 1, 0, false);
}
} else {
if (e.isShiftKey() && lastSelected != getLastFocused()) {
select(listStore.indexOf(lastSelected), idx + 1, true);
grid.getView().focusCell(idx + 1, 0, false);
} else {
if (idx + 1 < listStore.getCount()) {
selectNext(e.isShiftKey());
grid.getView().focusCell(idx + 1, 0, false);
}
}
}
}
}
e.preventDefault();
}
protected void onKeyLeft(GridEvent<M> ce) {
if (GXT.isFocusManagerEnabled() && selectedHeader != null) {
ColumnHeader ch = grid.getView().getHeader();
int idx = ch.indexOf(selectedHeader) - 1;
ColumnConfig config = grid.getColumnModel().getColumn(idx);
while (config != null) {
if (!config.isHidden()) {
Head h = getHead(idx, false);
selectedHeader = h;
grid.getView().getHeader().selectHeader(idx);
break;
} else {
idx--;
config = grid.getColumnModel().getColumn(idx);
}
}
}
if (GXT.isFocusManagerEnabled() && selectedGroup != null) {
groupingView.toggleGroup(selectedGroup, false);
}
}
@SuppressWarnings("unchecked")
protected void onKeyPress(GridEvent<M> e) {
int kc = e.getKeyCode();
if (GXT.isFocusManagerEnabled()) {
if (selectedHeader != null) {
if (kc == KeyCodes.KEY_ENTER) {
grid.getView().onHeaderClick((Grid) grid, grid.getColumnModel().indexOf(selectedHeader.config));
return;
} else if (kc == 32) {
ColumnHeader ch = grid.getView().getHeader();
int idx = ch.indexOf(selectedHeader);
String id = grid.getColumnModel().getColumnId(idx);
if (id == null || !id.equals("checker")) {
grid.getView().getHeader().showColumnMenu(idx);
}
return;
}
}
}
if (lastSelected != null && enableNavKeys) {
if (kc == KeyCodes.KEY_PAGEUP) {
e.stopEvent();
select(0, false);
grid.getView().focusRow(0);
} else if (kc == KeyCodes.KEY_PAGEDOWN) {
e.stopEvent();
int idx = listStore.indexOf(listStore.getAt(listStore.getCount() - 1));
select(idx, false);
grid.getView().focusRow(idx);
}
}
// if space bar is pressed
if (e.getKeyCode() == 32) {
if (getLastFocused() != null) {
if (e.isShiftKey() && lastSelected != null) {
int last = listStore.indexOf(lastSelected);
int i = listStore.indexOf(getLastFocused());
select(last, i, e.isControlKey());
grid.getView().focusCell(i, 0, false);
} else {
if (isSelected(getLastFocused())) {
deselect(getLastFocused());
} else {
select(getLastFocused(), true);
grid.getView().focusCell(listStore.indexOf(getLastFocused()), 0, false);
}
}
}
}
}
protected void onKeyRight(GridEvent<M> ce) {
if (GXT.isFocusManagerEnabled() && selectedHeader != null) {
ColumnHeader ch = grid.getView().getHeader();
int idx = ch.indexOf(selectedHeader) + 1;
ColumnConfig config = grid.getColumnModel().getColumn(idx);
while (config != null) {
if (!config.isHidden()) {
Head h = getHead(idx, false);
selectedHeader = h;
grid.getView().getHeader().selectHeader(idx);
break;
} else {
idx++;
config = grid.getColumnModel().getColumn(idx);
}
}
}
if (GXT.isAriaEnabled() && selectedGroup != null) {
groupingView.toggleGroup(selectedGroup, true);
}
}
protected void onKeyUp(GridEvent<M> e) {
if (GXT.isFocusManagerEnabled()) {
if (selectedHeader != null) {
return;
}
if (listStore.indexOf(lastSelected) == 0 && !grouped) {
deselectAll();
ColumnHeader header = grid.getView().getHeader();
Head h = getHead(0, false);
if (h != null) {
selectedHeader = h;
header.selectHeader(header.indexOf(h));
}
}
if (grouped) {
GroupingView view = (GroupingView) grid.getView();
NodeList<Element> groups = view.getGroups().cast();
if (selectedGroup != null) {
int gindex = indexOf(groups, selectedGroup);
if (gindex == 0) {
deselectAll();
ColumnHeader header = grid.getView().getHeader();
Head h = getHead(0, false);
if (h != null) {
selectedHeader = h;
header.selectHeader(header.indexOf(h));
}
}
view.onGroupSelect(selectedGroup, false);
selectedGroup = null;
if (gindex > 0) {
selectedGroup = groups.getItem(gindex - 1);
if (view.isExpanded(selectedGroup)) {
int grows = view.getGroupRowCount(selectedGroup);
Element r = view.getGroupRow(selectedGroup, grows - 1).cast();
selectedGroup = null;
select(view.findRowIndex(r), false);
view.focusRow(view.findRowIndex(r));
} else {
view.onGroupSelect(selectedGroup, true);
}
return;
}
}
if (lastSelected != null) {
Element row = view.getRow(lastSelected).cast();
Element group = view.findGroup(row).cast();
if (row == view.getGroupRow(group, 0)) {
deselectAll();
selectedGroup = group;
view.onGroupSelect(selectedGroup, true);
return;
}
}
}
}
int idx = listStore.indexOf(getLastFocused());
if (idx >= 0) {
if (e.isControlKey() || (e.isShiftKey() && isSelected(listStore.getAt(idx - 1)))) {
if (!e.isControlKey()) {
deselect(idx);
}
M lF = listStore.getAt(idx - 1);
if (lF != null) {
setLastFocused(lF);
grid.getView().focusCell(idx - 1, 0, false);
}
} else {
if (e.isShiftKey() && lastSelected != getLastFocused()) {
select(listStore.indexOf(lastSelected), idx - 1, true);
grid.getView().focusCell(idx - 1, 0, false);
} else {
if (idx > 0) {
selectPrevious(e.isShiftKey());
grid.getView().focusCell(idx - 1, 0, false);
}
}
}
}
e.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);
}
}
}
protected void onRowUpdated(GridEvent<M> ge) {
if (isSelected(ge.getModel())) {
onSelectChange(ge.getModel(), true);
}
if (getLastFocused() == ge.getModel()) {
setLastFocused(getLastFocused());
}
}
@Override
protected void onSelectChange(M model, boolean select) {
int idx = listStore.indexOf(model);
if (idx != -1) {
if (GXT.isFocusManagerEnabled() && selectedHeader != null) {
selectedHeader = null;
FocusFrame.get().frame(grid);
}
if (select) {
grid.getView().onRowSelect(idx);
} else {
grid.getView().onRowDeselect(idx);
}
}
}
private Head getHead(int index, boolean back) {
ColumnHeader header = grid.getView().getHeader();
int cols = grid.getColumnModel().getColumnCount();
if (!back) {
for (int i = index; i < cols; i++) {
ColumnConfig config = grid.getColumnModel().getColumn(i);
if (!config.isHidden() && !config.ariaIgnore) {
return header.getHead(i);
}
}
} else {
for (int i = index; i > -1; i--) {
ColumnConfig config = grid.getColumnModel().getColumn(i);
if (!config.isHidden() && !config.ariaIgnore) {
return header.getHead(i);
}
}
}
return null;
}
private int indexOf(NodeList<Element> elems, Element elem) {
for (int i = 0; i < elems.getLength(); i++) {
if (elems.getItem(i) == elem) {
return i;
}
}
return -1;
}
}