com.extjs.gxt.ui.client.widget.grid.Grid Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gxt Show documentation
Show all versions of gxt Show documentation
Rich Internet Application Framework for GWT
/*
* Sencha GXT 2.3.1a - Sencha for GWT
* Copyright(c) 2007-2013, Sencha, Inc.
* [email protected]
*
* http://www.sencha.com/products/gxt/license/
*/
package com.extjs.gxt.ui.client.widget.grid;
import java.util.HashMap;
import java.util.Map;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.aria.FocusFrame;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelProcessor;
import com.extjs.gxt.ui.client.data.ModelStringProvider;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.BoxComponent;
import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel.Callback;
import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel.Cell;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Accessibility;
/**
* This class represents the primary interface of a component based grid
* control. The grid requires a ListStore
and
* ColumnModel
when constructed. Each model in the store will be
* rendered as a row in the grid. Any updates to the store are automatically
* pushed to the grid. This includes inserting, removing, sorting and filter.
*
*
* Grid support several ways to manage column widths.
*
*
* - The most basic approach is to simply give pixel widths to each column.
* Columns widths will match the specified values.
* - A column can be set to "fill" all available space. As the width of the
* grid changes, or columns are resized, the "filling" column's width is
* adjusted so that the column's fill the available width with no horizontal
* scrolling. See @link {@link Grid#setAutoExpandColumn(String)}.
* - Grid can resize columns based on a "weight". As the width of the grid, or
* columns change, the "weight" is used to allocate the extra space, or the
* space needed to be reduced. Use {@link GridView#setAutoFill(boolean)} to
* enable this feature. With auto fill, the calculations are only run once.
* After the grid is rendered, the columns widths will not be adjusted when
* available width changes. You can use @link
* {@link GridView#setForceFit(boolean)} to always run the width calculations on
* any changes to available width or column sizes. Columns can be "fixed" which
* prevents their columns widths to be adjusted by the grid "weight"
* calculations. See @link {@link ColumnConfig#setFixed(boolean)}.
*
*
*
* When state is enabled (default is false), Grid will save and restore the
* column width, column hidden state, sort direction, and sort field. To enable
* state, see {@link #setStateful(boolean)}. When the store uses a
* PagingListLoader
the offset and limit parameter are saved with
* the Grid's state. These 2 values can be retrieved and used to make the first
* load request to return the user to the same location they left the grid.
*
* Code snippet:
*
*
PagingLoadConfig config = new BasePagingLoadConfig();
config.setOffset(0);
config.setLimit(50);
Map state = grid.getState();
if (state.containsKey("offset")) {
int offset = (Integer)state.get("offset");
int limit = (Integer)state.get("limit");
config.setOffset(offset);
config.setLimit(limit);
}
if (state.containsKey("sortField")) {
config.setSortField((String)state.get("sortField"));
config.setSortDir(SortDir.valueOf((String)state.get("sortDir")));
}
loader.load(config);
*
*
*
* - Events:
*
* - CellClick : GridEvent(grid, rowIndex, cellIndex, event)
* Fires after a cell is clicked.
*
* - grid : this
* - rowIndex : row index
* - cellIndex : cell index
* - event : the dom event
*
*
*
* - CellDoubleClick : GridEvent(grid, rowIndex, cellIndex, event)
* Fires after a cell is double clicked.
*
* - grid : this
* - rowIndex : row index
* - cellIndex : cell index
* - event : the dom event
*
*
*
* - CellMouseDown : GridEvent(grid, rowIndex, cellIndex, event)
* Fires before a cell is clicked.
*
* - grid : this
* - rowIndex : row index
* - cellIndex : cell index
* - event : the dom event
*
*
*
* - RowClick : GridEvent(grid, rowIndex, cellIndex, event)
* Fires after a row is clicked.
*
* - grid : this
* - rowIndex : the row index
* - cellIndex : cell index
* - index : the cell index
* - event : the dom event
*
*
*
* - RowDoubleClick : GridEvent(grid, rowIndex, cellIndex, event)
* Fires after a row is double clicked.
*
* - grid : this
* - rowIndex : the row index
* - index : the cell index
* - event : the dom event
*
*
*
* - RowMouseDown : GridEvent(grid, rowIndex, colIndex, event)
* Fires before a row is clicked.
*
* - grid : this
* - rowIndex : row index
* - colIndex : column index
* - event : the dom event
*
*
*
* - HeaderClick : GridEvent(grid, rowIndex, colIndex, event)
* Fires a header is clicked.
*
* - grid : this
* - rowIndex : row index
* - colIndex : column index
* - event : the dom event
*
*
*
* - HeaderDoubleClick : GridEvent(grid, rowIndex, colIndex, event)
* Fires a header is double clicked.
*
* - grid : this
* - rowIndex : row index
* - colIndex : column index
* - event : the dom event
*
*
*
* - HeaderMouseDown : GridEvent(grid, rowIndex, colIndex, event)
* Fires before a header is clicked.
*
* - grid : this
* - rowIndex : row index
* - colIndex : column index
* - event : the dom event
*
*
*
* - ContextMenu : GridEvent(grid)
* Fires before the grid's context menu is shown. Listeners can cancel the
* action by calling {@link BaseEvent#setCancelled(boolean)}.
*
* - grid : this
*
*
*
* - HeaderContextMenu : GridEvent(grid, colIndex, menu)
* Fires right before the header's context menu is displayed. Listeners can
* cancel the action by calling {@link BaseEvent#setCancelled(boolean)}.
*
* - grid : this
* - colIndex : the column index
* - menu : the context menu
*
*
*
* - BodyScroll : GridEvent(grid, srollLeft, scrollTop)
* Fires when the body element is scrolled.
*
* - grid : this
* - scrollLeft : scrollLeft
* - scrollTop : scrollTop
*
*
*
* - ColumnResize : GridEvent(grid, colIndex, width)
* Fires when the user resizes a column.
*
* - grid : this
* - colIndex : the column index
* - width : the new column width
*
*
*
* - ColumnMove : GridEvent(grid, colIndex, size)
* Fires when the user moves a column.
*
* - grid : this
* - oldIndex : the old column index
* - newIndex : the new column index
*
*
*
* - SortChange : GridEvent(grid, sortInfo)
* Fires when the grid's store sort changes.
*
* - grid : this
* - sortInfo : the sort field and direction
*
*
*
* - ViewReady : GridEvent(grid)
* Fires when the grid's view is ready.
*
* - grid : this
*
*
*
* - Reconfigure : GridEvent(grid)
* Fires when the grid gets reconfigured.
*
* - grid : this
*
*
*
*
*
* - Inherited Events:
* - BoxComponent Move
* - BoxComponent Resize
* - Component Enable
* - Component Disable
* - Component BeforeHide
* - Component Hide
* - Component BeforeShow
* - Component Show
* - Component Attach
* - Component Detach
* - Component BeforeRender
* - Component Render
* - Component BrowserEvent
* - Component BeforeStateRestore
* - Component StateRestore
* - Component BeforeStateSave
* - Component SaveState
*
*
* @param the model type
*/
public class Grid extends BoxComponent {
protected ColumnModel cm;
protected EditorSupport editSupport;
protected GridSelectionModel sm;
protected ListStore store;
protected ModelStringProvider stringProvider;
protected GridView view;
protected boolean viewReady;
private String autoExpandColumn;
private int autoExpandMax = 500;
private int autoExpandMin = 25;
private boolean columnLines;
private boolean enableColumnReorder;
private boolean enableColumnResize = true;
private boolean hideHeaders;
private int lazyRowRender = 10;
private boolean loadMask;
private int minColumnWidth = 25;
private ModelProcessor modelProcessor;
private boolean stripeRows;
private boolean trackMouseOver = true;
private Map states = new HashMap();
/**
* Creates a new grid.
*
* @param store the data store
* @param cm the column model
*/
public Grid(ListStore store, ColumnModel cm) {
this.store = store;
this.cm = cm;
this.view = new GridView();
disabledStyle = null;
baseStyle = "x-grid-panel";
setSelectionModel(new GridSelectionModel());
disableTextSelection(true);
}
protected Grid() {
}
@Override
public void disableTextSelection(boolean disable) {
disableTextSelection = disable ? 1 : 0;
}
/**
* Returns the auto expand column id.
*
* @return the auto expand column id
*/
public String getAutoExpandColumn() {
return autoExpandColumn;
}
/**
* Returns the auto expand maximum width.
*
* @return the max width in pixels
*/
public int getAutoExpandMax() {
return autoExpandMax;
}
/**
* Returns the auto expand minimum width.
*
* @return the minimum width in pixels
*/
public int getAutoExpandMin() {
return autoExpandMin;
}
/**
* Returns the column model.
*
* @return the column model
*/
public ColumnModel getColumnModel() {
return cm;
}
/**
* Returns the time in ms after the rows get rendered.
*
* @return the lazy row rendering time
*/
public int getLazyRowRender() {
return lazyRowRender;
}
/**
* Returns the minimum column width.
*
* @return the min width in pixels
*/
public int getMinColumnWidth() {
return minColumnWidth;
}
/**
* Returns the model processor.
*
* @return the model processor
*/
public ModelProcessor getModelProcessor() {
return modelProcessor;
}
/**
* Returns the grid's selection model.
*
* @return the selection model
*/
public GridSelectionModel getSelectionModel() {
return sm;
}
/**
* Returns the grid's store.
*
* @return the store
*/
public ListStore getStore() {
return store;
}
/**
* Returns the grid's view.
*
* @return the grid view
*/
public GridView getView() {
return view;
}
/**
* Returns true if column lines are enabled.
*
* @return true if column lines are enabled
*/
public boolean isColumnLines() {
return columnLines;
}
/**
* Returns true if column reordering is enabled.
*
* @return true if enabled
*/
public boolean isColumnReordering() {
return enableColumnReorder;
}
/**
* Returns true if column resizing is enabled.
*
* @return true if resizing is enabled
*/
public boolean isColumnResize() {
return enableColumnResize;
}
/**
* Returns true if the header is hidden.
*
* @return true for hidden
*/
public boolean isHideHeaders() {
return hideHeaders;
}
/**
* Returns true if the load mask in enabled.
*
* @return the load mask state
*/
public boolean isLoadMask() {
return loadMask;
}
/**
* Returns true if row striping is enabled.
*
* @return the strip row state
*/
public boolean isStripeRows() {
return stripeRows;
}
/**
* Returns true if rows are highlighted on mouse over.
*
* @return the track mouse state
*/
public boolean isTrackMouseOver() {
return trackMouseOver;
}
/**
* Returns true if the view is ready.
*
* @return the view ready state
*/
public boolean isViewReady() {
return viewReady;
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public void onComponentEvent(ComponentEvent ce) {
super.onComponentEvent(ce);
GridEvent ge = (GridEvent) ce;
switch (ce.getEventTypeInt()) {
case Event.ONCLICK:
onClick(ge);
break;
case Event.ONDBLCLICK:
onDoubleClick(ge);
break;
case Event.ONMOUSEDOWN:
onMouseDown(ge);
break;
case Event.ONMOUSEUP:
onMouseUp(ge);
break;
case Event.ONFOCUS:
onFocus(ce);
break;
case Event.ONBLUR:
onBlur(ce);
break;
}
view.handleComponentEvent(ge);
}
/**
* Reconfigures the grid to use a different Store and Column Model. The View
* will be bound to the new objects and refreshed.
*
* @param store the new store
* @param cm the new column model
*/
public void reconfigure(ListStore store, ColumnModel cm) {
if (loadMask && rendered) {
mask(GXT.MESSAGES.loadMask_msg());
}
if (rendered) {
view.initData(store, cm);
}
this.store = store;
this.cm = cm;
// rebind the sm
setSelectionModel(sm);
if (isViewReady()) {
view.refresh(true);
}
if (loadMask && rendered) {
unmask();
}
fireEvent(Events.Reconfigure);
}
/**
* The id of a column in this grid that should expand to fill unused space
* (pre-render). This id can not be 0.
*
* @param autoExpandColumn the auto expand column id
*/
public void setAutoExpandColumn(String autoExpandColumn) {
this.autoExpandColumn = autoExpandColumn;
}
/**
* The maximum width the autoExpandColumn can have (if enabled) (defaults to
* 500, pre-render).
*
* @param autoExpandMax the auto expand max
*/
public void setAutoExpandMax(int autoExpandMax) {
this.autoExpandMax = autoExpandMax;
}
/**
* The minimum width the autoExpandColumn can have (if enabled)(pre-render).
*
* @param autoExpandMin the auto expand min width
*/
public void setAutoExpandMin(int autoExpandMin) {
this.autoExpandMin = autoExpandMin;
}
/**
* True to enable column separation lines (defaults to false).
*
* @param columnLines true to enable column separation lines
*/
public void setColumnLines(boolean columnLines) {
this.columnLines = columnLines;
if (rendered) {
el().setStyleName("x-grid-with-col-lines", columnLines);
}
}
/**
* True to enable column reordering via drag and drop (defaults to false).
*
* @param enableColumnReorder true to enable
*/
public void setColumnReordering(boolean enableColumnReorder) {
this.enableColumnReorder = enableColumnReorder;
}
/**
* Sets whether columns may be resized (defaults to true).
*
* @param enableColumnResize true to allow column resizing
*/
public void setColumnResize(boolean enableColumnResize) {
this.enableColumnResize = enableColumnResize;
}
/**
* Sets whether the header should be hidden (defaults to false).
*
* @param hideHeaders true to hide the header
*/
public void setHideHeaders(boolean hideHeaders) {
this.hideHeaders = hideHeaders;
}
/**
* Sets the time in ms after the row gets rendered (defaults to 10). 0 means
* that the rows get rendered as soon as the grid gets rendered.
*
* @param lazyRowRender the time in ms after the rows get rendered.
*/
public void setLazyRowRender(int lazyRowRender) {
this.lazyRowRender = lazyRowRender;
}
/**
* Sets whether a load mask should be displayed during load operations
* (defaults to false).
*
* @param loadMask true to show a mask
*/
public void setLoadMask(boolean loadMask) {
this.loadMask = loadMask;
}
/**
* The minimum width a column can be resized to (defaults to 25).
*
* @param minColumnWidth the min column width
*/
public void setMinColumnWidth(int minColumnWidth) {
this.minColumnWidth = minColumnWidth;
}
/**
* Sets the grid's model processor.
*
* @see ModelProcessor
* @param modelProcessor
*/
public void setModelProcessor(ModelProcessor modelProcessor) {
this.modelProcessor = modelProcessor;
}
/**
* Sets the grid selection model.
*
* @param sm the selection model
*/
public void setSelectionModel(GridSelectionModel sm) {
if (this.sm != null) {
this.sm.bindGrid(null);
}
this.sm = sm;
if (sm != null) {
sm.bindGrid(this);
}
}
/**
* Sets the binder's string provider.
*
* @param stringProvider the string provider
*/
public void setStringProvider(ModelStringProvider stringProvider) {
this.stringProvider = stringProvider;
}
/**
* True to stripe the rows (defaults to false).
*
* @param stripeRows true to strip rows
*/
public void setStripeRows(boolean stripeRows) {
this.stripeRows = stripeRows;
}
/**
* True to highlight rows when the mouse is over (defaults to true).
*
* @param trackMouseOver true to highlight rows on mouse over
*/
public void setTrackMouseOver(boolean trackMouseOver) {
this.trackMouseOver = trackMouseOver;
}
/**
* Sets the view's grid (pre-render).
*
* @param view the view
*/
public void setView(GridView view) {
this.view = view;
// rebind the sm
setSelectionModel(sm);
}
protected void afterRender() {
view.render();
super.afterRender();
if (lazyRowRender > 0) {
Timer t = new Timer() {
@Override
public void run() {
afterRenderView();
}
};
t.schedule(lazyRowRender);
} else {
afterRenderView();
}
}
protected void afterRenderView() {
viewReady = true;
view.afterRender();
onAfterRenderView();
for (String key : states.keySet()) {
setAriaState(key, states.get(key));
}
fireEvent(Events.ViewReady);
}
@Override
protected void applyState(Map state) {
super.applyState(state);
if (isStateful()) {
for (ColumnConfig c : cm.getColumns()) {
String id = c.getId();
if (state.containsKey("hidden" + id)) {
c.setHidden((Boolean) state.get("hidden" + id));
}
if (state.containsKey("width" + id)) {
c.setWidth((Integer) state.get("width" + id));
}
}
doApplyStoreState(state);
}
}
@Override
protected ComponentEvent createComponentEvent(Event event) {
return view.createComponentEvent(event);
}
protected void doApplyStoreState(Map state) {
String sortField = (String) state.get("sortField");
if ((store.getLoader() == null || !store.getLoader().isRemoteSort()) && sortField != null) {
String sortDir = (String) state.get("sortDir");
SortDir dir = SortDir.findDir(sortDir);
store.sort(sortField, dir);
}
}
@Override
protected void doAttachChildren() {
super.doAttachChildren();
view.doAttach();
}
@Override
protected void doDetachChildren() {
super.doDetachChildren();
view.doDetach();
}
protected EditorSupport getEditSupport() {
return new EditorSupport();
}
@Override
protected El getFocusEl() {
if (isViewReady()) {
return view.focusEl;
} else {
return super.getFocusEl();
}
}
@Override
protected void notifyHide() {
super.notifyHide();
view.notifyHide();
}
@Override
protected void notifyShow() {
super.notifyShow();
view.notifyShow();
}
protected void onAfterRenderView() {
}
protected void onBlur(ComponentEvent ce) {
if (GXT.isFocusManagerEnabled()) {
FocusFrame.get().unframe();
}
}
protected void onClick(GridEvent e) {
if (e.getRowIndex() != -1) {
fireEvent(Events.RowClick, e);
if (e.getColIndex() != -1) {
fireEvent(Events.CellClick, e);
}
}
}
@Override
protected void onDisable() {
super.onDisable();
mask();
}
protected void onDoubleClick(GridEvent e) {
if (e.getRowIndex() != -1) {
fireEvent(Events.RowDoubleClick, e);
if (e.getColIndex() != -1) {
fireEvent(Events.CellDoubleClick, e);
}
}
}
@Override
protected void onEnable() {
super.onEnable();
unmask();
}
protected void onFocus(ComponentEvent ce) {
if (GXT.isFocusManagerEnabled()) {
if (getSelectionModel().selectedHeader != null) {
FocusFrame.get().frame(getSelectionModel().selectedHeader);
} else {
FocusFrame.get().frame(this);
}
}
}
protected void onMouseDown(GridEvent e) {
if (isDisableTextSelection() && GXT.isWebKit) {
String tagName = e.getEvent().getEventTarget(). cast().getTagName();
if (!"input".equalsIgnoreCase(tagName) && !"textarea".equalsIgnoreCase(tagName)) {
e.preventDefault();
}
}
if (e.getRowIndex() != -1) {
fireEvent(Events.RowMouseDown, e);
if (e.getColIndex() != -1) {
fireEvent(Events.CellMouseDown, e);
}
}
}
protected void onMouseUp(GridEvent e) {
if (e.getRowIndex() != -1) {
fireEvent(Events.RowMouseUp, e);
if (e.getColIndex() != -1) {
fireEvent(Events.CellMouseUp, e);
}
}
}
@Override
protected void onRender(Element target, int index) {
setElement(DOM.createDiv(), target, index);
super.onRender(target, index);
el().setStyleAttribute("position", "relative");
setColumnLines(isColumnLines());
view.init(this);
el().setTabIndex(0);
el().setElementAttribute("hideFocus", "true");
if (GXT.isAriaEnabled()) {
Accessibility.setRole(getElement(), "grid");
setAriaState("aria-readonly", "true");
setAriaState("aria-multiselectable", "true");
}
}
@Override
protected void onResize(int width, int height) {
super.onResize(width, height);
if (viewReady) {
view.calculateVBar(true);
} else {
view.layout();
}
}
@Override
protected void setAriaRole(String roleName) {
if (isViewReady()) {
Accessibility.setRole(view.focusEl.dom, roleName);
}
}
protected void setAriaState(String stateName, String stateValue) {
if (isViewReady()) {
Accessibility.setState(view.focusEl.dom, stateName, stateValue);
} else {
states.put(stateName, stateValue);
}
}
protected Cell walkCells(int row, int col, int step, Callback callback, boolean acceptNavs) {
boolean first = true;
int clen = cm.getColumnCount();
int rlen = store.getCount();
if (step < 0) {
if (col < 0) {
if (GXT.isFocusManagerEnabled()) {
return new Cell(row, 0);
}
row--;
first = false;
}
while (row >= 0) {
if (!first) {
col = clen - 1;
}
first = false;
while (col >= 0) {
if (callback.isSelectable(row, col, acceptNavs)) {
return new Cell(row, col);
}
col--;
}
row--;
}
} else {
if (col == clen && GXT.isFocusManagerEnabled()) {
return new Cell(row, col - 1);
}
if (col >= clen) {
row++;
first = false;
}
while (row < rlen) {
if (!first) {
col = 0;
}
first = false;
while (col < clen) {
if (callback.isSelectable(row, col, acceptNavs)) {
return new Cell(row, col);
}
col++;
}
row++;
}
}
return null;
}
}